1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
|
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pkix
import (
"fmt"
"strings"
ldapv3 "github.com/go-ldap/ldap/v3"
)
// ParseDistinguishedName parses a DN name and validates Notary Project rules
func ParseDistinguishedName(name string) (map[string]string, error) {
if strings.Contains(name, "=#") {
return nil, fmt.Errorf("unsupported distinguished name (DN) %q: notation does not support x509.subject identities containing \"=#\"", name)
}
attrKeyValue := make(map[string]string)
dn, err := ldapv3.ParseDN(name)
if err != nil {
return nil, fmt.Errorf("parsing distinguished name (DN) %q failed with err: %v. A valid DN must contain 'C', 'ST' or 'S', and 'O' RDN attributes at a minimum, and follow RFC 4514 standard", name, err)
}
for _, rdn := range dn.RDNs {
// multi-valued RDNs are not supported (TODO: add spec reference here)
if len(rdn.Attributes) > 1 {
return nil, fmt.Errorf("distinguished name (DN) %q has multi-valued RDN attributes, remove multi-valued RDN attributes as they are not supported", name)
}
for _, attribute := range rdn.Attributes {
// stateOrProvince name 'S' is an alias for 'ST'
if attribute.Type == "S" {
attribute.Type = "ST"
}
if attrKeyValue[attribute.Type] == "" {
attrKeyValue[attribute.Type] = attribute.Value
} else {
return nil, fmt.Errorf("distinguished name (DN) %q has duplicate RDN attribute for %q, DN can only have unique RDN attributes", name, attribute.Type)
}
}
}
// Verify mandatory fields are present
mandatoryFields := []string{"C", "ST", "O"}
for _, field := range mandatoryFields {
if attrKeyValue[field] == "" {
return nil, fmt.Errorf("distinguished name (DN) %q has no mandatory RDN attribute for %q, it must contain 'C', 'ST' or 'S', and 'O' RDN attributes at a minimum", name, field)
}
}
// No errors
return attrKeyValue, nil
}
// IsSubsetDN returns true if dn1 is a subset of dn2 i.e. every key/value pair
// of dn1 has a matching key/value pair in dn2, otherwise returns false
func IsSubsetDN(dn1 map[string]string, dn2 map[string]string) bool {
for key := range dn1 {
if dn1[key] != dn2[key] {
return false
}
}
return true
}
|