-
Notifications
You must be signed in to change notification settings - Fork 18.9k
crypto/x509/pkix: Name type does not sensibly generate RDNs #40876
Description
What version of Go are you using (go version)?
$ go version
1.14.7, specifically, the playground.
Does this issue reproduce with the latest release?
It reproduces on the playground, which uses the "latest stable release"
What operating system and processor architecture are you using (go env)?
go env Output
$ go env
What did you do?
package main
import (
"fmt"
"crypto/x509/pkix"
)
func main() {
// Adding >1 item to any of the arrays in pkix.Name results in golang representing them as a single multi-valued RDN,
// which is not valid. X.501 states,
// > a given attribute type cannot appear twice in the same RDN
// but that's exactly what Golang will always emit, for any array with >1 entry.
var name pkix.Name = pkix.Name {
OrganizationalUnit: []string{"foo", "bar"},
};
fmt.Println("OUs of {\"foo\", \"bar\"}, encoded:", name.ToRDNSequence(), " // this is a malformed multi-valued RDN");
var input_rdn_seq pkix.RDNSequence = []pkix.RelativeDistinguishedNameSET{
[]pkix.AttributeTypeAndValue { pkix.AttributeTypeAndValue { Type: []int{2, 5, 4, 11} /* OU */, Value: "foo" }},
[]pkix.AttributeTypeAndValue { pkix.AttributeTypeAndValue { Type: []int{2, 5, 4, 11} /* OU */, Value: "foo" }},
};
fmt.Println("RDN seq. representing OU=foo,OU=bar:", input_rdn_seq);
decoded_name := pkix.Name {};
decoded_name.FillFromRDNSequence(&input_rdn_seq);
fmt.Println(" same seq, decoded:", decoded_name, " // wrong");
fmt.Println(" same seq, re-encoded:", decoded_name.ToRDNSequence(), " // wrong")
}
What did you expect to see / what you saw instead?
First understand RDNs; LDAPwiki is a good source, particularly,
Relative Distinguished Name is comprised of one or more name-value pairs, in which the name and the value are separated by an equal sign (e.g., for an RDN of "uid=ann", the name is "uid" and the value is "ann"), and if there are multiple name-value pairs then they should be separated by plus signs (e.g., for an RDN of "cn=Jon Doe+employeeNumber=12345", the name-value pairs are "cn=John Doe" and "employeeNumber=12345").
In practice, Relative Distinguished Name containing multiple name-value pairs (called "Multi-Valued RDNs") are rare, but they can be useful at times when either there is no unique attribute in the entry or you want to ensure that the entry's Distinguished Name contains some useful identifying information.
The above program uses >1 entry in the Name.OrganizationUnit field. When pkix.Name encodes this into an RDNSequence, either through calling ToRDNSequence or by simply printing it, go will encode all the given OUs as a single multi-valued RDN; the source code to pkix even explicity notes this,
// ToRDNSequence converts n into a single RDNSequence. The following
// attributes are encoded as multi-value RDNs:
//
// - Country
// - Organization
// - OrganizationalUnit
// - Locality
// - Province
// - StreetAddress
// - PostalCode
Such RDNs are always invalid, though; in an RDN, each AttributeTypeAndValue must have a different AttributeType. From X.501¹,
The set that forms an RDN contains exactly one
AttributeTypeAndValuefor each attribute which contains distinguished values in the entry; that is, a given attribute type cannot appear twice in the same RDN.
Regardless, an RDN represents a hierarchical reference to an entity of some sort. Multiple OUs, in particular, is going to refer to one OU nested inside of another.
Further, pkix.Name's layout doesn't preserve ordering of the input RDN; not only will it fail to round trip an RDNSequence with two RDNs containing the same AttributeType, it also can't even round-trip the example from the standard:
Example from the standard: CN=Smith,OU=Sales+L=Ipswitch,O=Telecom,C=GB
Example from the standard, as pkix.Name: CN=Smith,OU=Sales,O=Telecom,L=Ipswitch,C=GB
The package sort of notes this,
Name is only an approximation of the X.509 structure. If an accurate representation is needed, asn1.Unmarshal the raw subject or issuer as an
RDNSequence.
But it hands a most likely unaware user a loaded gun of an API. E.g., cert-manager, a project to issues certificates in Kubernetes clusters, adopted the structure of pkix.Name nearly verbatim to represent RDN sequences. Since it uses pkix.Name nearly directly, it suffers all the same flaws: some RDN sequences are impossible to input, and some input result in invalid nonsensical RDN sequences. A high-level API should not guide the user towards code that produces incorrect results.
¹Note that this is a superseded version of the standard; as X.501 is not an open standard, it is not possible to link to it.
