get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/internal/ldap/dn.go (about) 1 // Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com) 2 // Portions copyright (c) 2015-2016 go-ldap Authors 3 package ldap 4 5 import ( 6 "bytes" 7 "crypto/x509/pkix" 8 "encoding/asn1" 9 enchex "encoding/hex" 10 "errors" 11 "fmt" 12 "strings" 13 ) 14 15 var attributeTypeNames = map[string]string{ 16 "2.5.4.3": "CN", 17 "2.5.4.5": "SERIALNUMBER", 18 "2.5.4.6": "C", 19 "2.5.4.7": "L", 20 "2.5.4.8": "ST", 21 "2.5.4.9": "STREET", 22 "2.5.4.10": "O", 23 "2.5.4.11": "OU", 24 "2.5.4.17": "POSTALCODE", 25 // FIXME: Add others. 26 "0.9.2342.19200300.100.1.25": "DC", 27 } 28 29 // AttributeTypeAndValue represents an attributeTypeAndValue from https://tools.ietf.org/html/rfc4514 30 type AttributeTypeAndValue struct { 31 // Type is the attribute type 32 Type string 33 // Value is the attribute value 34 Value string 35 } 36 37 // RelativeDN represents a relativeDistinguishedName from https://tools.ietf.org/html/rfc4514 38 type RelativeDN struct { 39 Attributes []*AttributeTypeAndValue 40 } 41 42 // DN represents a distinguishedName from https://tools.ietf.org/html/rfc4514 43 type DN struct { 44 RDNs []*RelativeDN 45 } 46 47 // FromCertSubject takes a pkix.Name from a cert and returns a DN 48 // that uses the same set. Does not support multi value RDNs. 49 func FromCertSubject(subject pkix.Name) (*DN, error) { 50 dn := &DN{ 51 RDNs: make([]*RelativeDN, 0), 52 } 53 for i := len(subject.Names) - 1; i >= 0; i-- { 54 name := subject.Names[i] 55 oidString := name.Type.String() 56 typeName, ok := attributeTypeNames[oidString] 57 if !ok { 58 return nil, fmt.Errorf("invalid type name: %+v", name) 59 } 60 v, ok := name.Value.(string) 61 if !ok { 62 return nil, fmt.Errorf("invalid type value: %+v", v) 63 } 64 rdn := &RelativeDN{ 65 Attributes: []*AttributeTypeAndValue{ 66 { 67 Type: typeName, 68 Value: v, 69 }, 70 }, 71 } 72 dn.RDNs = append(dn.RDNs, rdn) 73 } 74 return dn, nil 75 } 76 77 // FromRawCertSubject takes a raw subject from a certificate 78 // and uses asn1.Unmarshal to get the individual RDNs in the 79 // original order, including multi-value RDNs. 80 func FromRawCertSubject(rawSubject []byte) (*DN, error) { 81 dn := &DN{ 82 RDNs: make([]*RelativeDN, 0), 83 } 84 var rdns pkix.RDNSequence 85 _, err := asn1.Unmarshal(rawSubject, &rdns) 86 if err != nil { 87 return nil, err 88 } 89 90 for i := len(rdns) - 1; i >= 0; i-- { 91 rdn := rdns[i] 92 if len(rdn) == 0 { 93 continue 94 } 95 96 r := &RelativeDN{} 97 attrs := make([]*AttributeTypeAndValue, 0) 98 for j := len(rdn) - 1; j >= 0; j-- { 99 atv := rdn[j] 100 101 typeName := "" 102 name := atv.Type.String() 103 typeName, ok := attributeTypeNames[name] 104 if !ok { 105 return nil, fmt.Errorf("invalid type name: %+v", name) 106 } 107 value, ok := atv.Value.(string) 108 if !ok { 109 return nil, fmt.Errorf("invalid type value: %+v", atv.Value) 110 } 111 attr := &AttributeTypeAndValue{ 112 Type: typeName, 113 Value: value, 114 } 115 attrs = append(attrs, attr) 116 } 117 r.Attributes = attrs 118 dn.RDNs = append(dn.RDNs, r) 119 } 120 121 return dn, nil 122 } 123 124 // ParseDN returns a distinguishedName or an error. 125 // The function respects https://tools.ietf.org/html/rfc4514 126 func ParseDN(str string) (*DN, error) { 127 dn := new(DN) 128 dn.RDNs = make([]*RelativeDN, 0) 129 rdn := new(RelativeDN) 130 rdn.Attributes = make([]*AttributeTypeAndValue, 0) 131 buffer := bytes.Buffer{} 132 attribute := new(AttributeTypeAndValue) 133 escaping := false 134 135 unescapedTrailingSpaces := 0 136 stringFromBuffer := func() string { 137 s := buffer.String() 138 s = s[0 : len(s)-unescapedTrailingSpaces] 139 buffer.Reset() 140 unescapedTrailingSpaces = 0 141 return s 142 } 143 144 for i := 0; i < len(str); i++ { 145 char := str[i] 146 switch { 147 case escaping: 148 unescapedTrailingSpaces = 0 149 escaping = false 150 switch char { 151 case ' ', '"', '#', '+', ',', ';', '<', '=', '>', '\\': 152 buffer.WriteByte(char) 153 continue 154 } 155 // Not a special character, assume hex encoded octet 156 if len(str) == i+1 { 157 return nil, errors.New("got corrupted escaped character") 158 } 159 160 dst := []byte{0} 161 n, err := enchex.Decode([]byte(dst), []byte(str[i:i+2])) 162 if err != nil { 163 return nil, fmt.Errorf("failed to decode escaped character: %s", err) 164 } else if n != 1 { 165 return nil, fmt.Errorf("expected 1 byte when un-escaping, got %d", n) 166 } 167 buffer.WriteByte(dst[0]) 168 i++ 169 case char == '\\': 170 unescapedTrailingSpaces = 0 171 escaping = true 172 case char == '=': 173 attribute.Type = stringFromBuffer() 174 // Special case: If the first character in the value is # the following data 175 // is BER encoded. Throw an error since not supported right now. 176 if len(str) > i+1 && str[i+1] == '#' { 177 return nil, errors.New("unsupported BER encoding") 178 } 179 case char == ',' || char == '+': 180 // We're done with this RDN or value, push it 181 if len(attribute.Type) == 0 { 182 return nil, errors.New("incomplete type, value pair") 183 } 184 attribute.Value = stringFromBuffer() 185 rdn.Attributes = append(rdn.Attributes, attribute) 186 attribute = new(AttributeTypeAndValue) 187 if char == ',' { 188 dn.RDNs = append(dn.RDNs, rdn) 189 rdn = new(RelativeDN) 190 rdn.Attributes = make([]*AttributeTypeAndValue, 0) 191 } 192 case char == ' ' && buffer.Len() == 0: 193 // ignore unescaped leading spaces 194 continue 195 default: 196 if char == ' ' { 197 // Track unescaped spaces in case they are trailing and we need to remove them 198 unescapedTrailingSpaces++ 199 } else { 200 // Reset if we see a non-space char 201 unescapedTrailingSpaces = 0 202 } 203 buffer.WriteByte(char) 204 } 205 } 206 if buffer.Len() > 0 { 207 if len(attribute.Type) == 0 { 208 return nil, errors.New("DN ended with incomplete type, value pair") 209 } 210 attribute.Value = stringFromBuffer() 211 rdn.Attributes = append(rdn.Attributes, attribute) 212 dn.RDNs = append(dn.RDNs, rdn) 213 } 214 return dn, nil 215 } 216 217 // Equal returns true if the DNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch). 218 // Returns true if they have the same number of relative distinguished names 219 // and corresponding relative distinguished names (by position) are the same. 220 func (d *DN) Equal(other *DN) bool { 221 if len(d.RDNs) != len(other.RDNs) { 222 return false 223 } 224 for i := range d.RDNs { 225 if !d.RDNs[i].Equal(other.RDNs[i]) { 226 return false 227 } 228 } 229 return true 230 } 231 232 // RDNsMatch returns true if the individual RDNs of the DNs 233 // are the same regardless of ordering. 234 func (d *DN) RDNsMatch(other *DN) bool { 235 if len(d.RDNs) != len(other.RDNs) { 236 return false 237 } 238 239 CheckNextRDN: 240 for _, irdn := range d.RDNs { 241 for _, ordn := range other.RDNs { 242 if (len(irdn.Attributes) == len(ordn.Attributes)) && 243 (irdn.hasAllAttributes(ordn.Attributes) && ordn.hasAllAttributes(irdn.Attributes)) { 244 // Found the RDN, check if next one matches. 245 continue CheckNextRDN 246 } 247 } 248 249 // Could not find a matching individual RDN, auth fails. 250 return false 251 } 252 return true 253 } 254 255 // AncestorOf returns true if the other DN consists of at least one RDN followed by all the RDNs of the current DN. 256 // "ou=widgets,o=acme.com" is an ancestor of "ou=sprockets,ou=widgets,o=acme.com" 257 // "ou=widgets,o=acme.com" is not an ancestor of "ou=sprockets,ou=widgets,o=foo.com" 258 // "ou=widgets,o=acme.com" is not an ancestor of "ou=widgets,o=acme.com" 259 func (d *DN) AncestorOf(other *DN) bool { 260 if len(d.RDNs) >= len(other.RDNs) { 261 return false 262 } 263 // Take the last `len(d.RDNs)` RDNs from the other DN to compare against 264 otherRDNs := other.RDNs[len(other.RDNs)-len(d.RDNs):] 265 for i := range d.RDNs { 266 if !d.RDNs[i].Equal(otherRDNs[i]) { 267 return false 268 } 269 } 270 return true 271 } 272 273 // Equal returns true if the RelativeDNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch). 274 // Relative distinguished names are the same if and only if they have the same number of AttributeTypeAndValues 275 // and each attribute of the first RDN is the same as the attribute of the second RDN with the same attribute type. 276 // The order of attributes is not significant. 277 // Case of attribute types is not significant. 278 func (r *RelativeDN) Equal(other *RelativeDN) bool { 279 if len(r.Attributes) != len(other.Attributes) { 280 return false 281 } 282 return r.hasAllAttributes(other.Attributes) && other.hasAllAttributes(r.Attributes) 283 } 284 285 func (r *RelativeDN) hasAllAttributes(attrs []*AttributeTypeAndValue) bool { 286 for _, attr := range attrs { 287 found := false 288 for _, myattr := range r.Attributes { 289 if myattr.Equal(attr) { 290 found = true 291 break 292 } 293 } 294 if !found { 295 return false 296 } 297 } 298 return true 299 } 300 301 // Equal returns true if the AttributeTypeAndValue is equivalent to the specified AttributeTypeAndValue 302 // Case of the attribute type is not significant 303 func (a *AttributeTypeAndValue) Equal(other *AttributeTypeAndValue) bool { 304 return strings.EqualFold(a.Type, other.Type) && a.Value == other.Value 305 }