diff --git a/changes/31390-fix-certificate-ingest-parser b/changes/31390-fix-certificate-ingest-parser new file mode 100644 index 0000000000..2d479b28be --- /dev/null +++ b/changes/31390-fix-certificate-ingest-parser @@ -0,0 +1,2 @@ +* Fixed certificate ingest parser to no longer break on multiple equal signs in certificate key pair values +* Fixed certificate ingest parser to allow for only multiple relative distinguished names separated by + \ No newline at end of file diff --git a/server/fleet/host_certificates.go b/server/fleet/host_certificates.go index 367629df8a..00adc4495f 100644 --- a/server/fleet/host_certificates.go +++ b/server/fleet/host_certificates.go @@ -220,16 +220,22 @@ func ExtractDetailsFromOsqueryDistinguishedName(str string) (*HostCertificateNam str = strings.ReplaceAll(str, `\/`, `<>`) // Replace with our own "safe" sequence parts := strings.Split(str, "/") + if len(parts) == 1 { + // Try to split into parts based on + + parts = strings.Split(str, "+") + } + var details HostCertificateNameDetails for _, part := range parts { - kv := strings.Split(part, "=") - if len(kv) != 2 { + key, value, found := strings.Cut(part, "=") + + if !found { return nil, fmt.Errorf("invalid distinguished name, wrong key value pair format: %s", str) } - value := strings.ReplaceAll(strings.Trim(kv[1], " "), `<>`, `/`) // Replace our "safe" sequence with forward slash + value = strings.ReplaceAll(strings.Trim(value, " "), `<>`, `/`) // Replace our "safe" sequence with forward slash - switch strings.ToUpper(kv[0]) { + switch strings.ToUpper(key) { case "C": details.Country = strings.Trim(value, " ") case "O": diff --git a/server/fleet/host_certificates_test.go b/server/fleet/host_certificates_test.go index 72851e5865..4ce31c2ed8 100644 --- a/server/fleet/host_certificates_test.go +++ b/server/fleet/host_certificates_test.go @@ -7,17 +7,13 @@ import ( ) func TestExtractHostCertificateNameDetails(t *testing.T) { - expected := HostCertificateNameDetails{ - Country: "US", - Organization: "Fleet Device Management Inc.", - OrganizationalUnit: "Fleet Device Management Inc.", - CommonName: "FleetDM", - } - expectedWithSlash := HostCertificateNameDetails{ - Country: "US", - Organization: "Fleet Device Management Inc.", - OrganizationalUnit: "Fleet Device Management Inc.", - CommonName: "FleetDM/valid", + getExpectedHostCertificateDetails := func(commonName string) *HostCertificateNameDetails { + return &HostCertificateNameDetails{ + Country: "US", + Organization: "Fleet Device Management Inc.", + OrganizationalUnit: "Fleet Device Management Inc.", + CommonName: commonName, + } } cases := []struct { @@ -29,12 +25,12 @@ func TestExtractHostCertificateNameDetails(t *testing.T) { { name: "valid", input: "/C=US/O=Fleet Device Management Inc./OU=Fleet Device Management Inc./CN=FleetDM", - expected: &expected, + expected: getExpectedHostCertificateDetails("FleetDM"), }, { name: "valid with different order", input: "/O=Fleet Device Management Inc./OU=Fleet Device Management Inc./CN=FleetDM/C=US", - expected: &expected, + expected: getExpectedHostCertificateDetails("FleetDM"), }, { name: "valid with missing key", @@ -49,17 +45,22 @@ func TestExtractHostCertificateNameDetails(t *testing.T) { { name: "valid with additional keyr", input: "/C=US/O=Fleet Device Management Inc./OU=Fleet Device Management Inc./CN=FleetDM/L=SomeCity", - expected: &expected, + expected: getExpectedHostCertificateDetails("FleetDM"), }, { name: "valid format with extra slash", input: `/C=US/O=Fleet Device Management Inc./OU=Fleet Device Management Inc./CN=FleetDM\/valid`, - expected: &expectedWithSlash, + expected: getExpectedHostCertificateDetails("FleetDM/valid"), }, { name: "valid with safe escape sequence", input: `/C=US/O=Fleet Device Management Inc./OU=Fleet Device Management Inc./CN=FleetDM<>valid`, - expected: &expectedWithSlash, + expected: getExpectedHostCertificateDetails("FleetDM/valid"), + }, + { + name: "valid format with equal signs in value", + input: "/C=US/O=Fleet Device Management Inc./OU=Fleet Device Management Inc./CN=FleetDM=Company", + expected: getExpectedHostCertificateDetails("FleetDM=Company"), }, { name: "invalid format with extra slash without escape", @@ -67,14 +68,11 @@ func TestExtractHostCertificateNameDetails(t *testing.T) { err: true, }, { - name: "invalid format with wrong separator", + name: "format with wrong separator", // this will now just be treated as part of C input: "C=US,O=Fleet Device Management Inc.,OU=Fleet Device Management Inc.,CN=FleetDM", - err: true, - }, - { - name: "invalid format with extra equal", - input: "/C=US=/O=Fleet Device Management Inc./OU=Fleet Device Management Inc./CN=FleetDM", - err: true, + expected: &HostCertificateNameDetails{ + Country: "US,O=Fleet Device Management Inc.,OU=Fleet Device Management Inc.,CN=FleetDM", + }, }, { name: "invalid format with malformed key values", @@ -99,12 +97,12 @@ func TestExtractHostCertificateNameDetails(t *testing.T) { { name: "missing first slash", input: "C=US/O=Fleet Device Management Inc./OU=Fleet Device Management Inc./CN=FleetDM", - expected: &expected, + expected: getExpectedHostCertificateDetails("FleetDM"), }, { name: "trailing slash", input: "/C=US/O=Fleet Device Management Inc./OU=Fleet Device Management Inc./CN=FleetDM/", - expected: &expected, + expected: getExpectedHostCertificateDetails("FleetDM"), }, { name: "simple common name", @@ -127,9 +125,24 @@ func TestExtractHostCertificateNameDetails(t *testing.T) { }, }, { - name: "invalid separator", - input: "/C=US,O=Fleet Device Management Inc.,OU=Fleet Device Management Inc.,CN=FleetDM", - err: true, + name: "with plusses as separator", + input: "DN=something+CN=FleetDM+OU=Org", + expected: &HostCertificateNameDetails{ + Country: "", + Organization: "", + OrganizationalUnit: "Org", + CommonName: "FleetDM", + }, + }, + { + name: "with plusses inside values and slash as separator", + input: "DN=something/CN=FleetDM+valid/OU=Org", + expected: &HostCertificateNameDetails{ + Country: "", + Organization: "", + OrganizationalUnit: "Org", + CommonName: "FleetDM+valid", + }, }, } for _, tc := range cases {