1package ldap
2
3import (
4 "errors"
5 "strings"
6)
7
8var errDN = errors.New("ldap: unsupported distinguished name")
9
10type rdn struct {
11 typ string
12 val string
13}
14
15func parseDN(s string) ([]rdn, error) {
16 if s == "" {
17 return nil, nil
18 }
19 if strings.ContainsAny(s, "\\\"+#;<>") {
20 return nil, errDN
21 }
22 var out []rdn
23 for _, part := range strings.Split(s, ",") {
24 if strings.Count(part, "=") != 1 {
25 return nil, errDN
26 }
27 typ, val, _ := strings.Cut(part, "=")
28 typ = strings.ToLower(strings.Trim(typ, " "))
29 val = strings.ToLower(strings.Trim(val, " "))
30 if !dnToken(typ, false) || !dnToken(val, true) {
31 return nil, errDN
32 }
33 out = append(out, rdn{typ: typ, val: val})
34 }
35 return out, nil
36}
37
38func dnToken(s string, value bool) bool {
39 if s == "" || len(s) > 64 {
40 return false
41 }
42 for i := 0; i < len(s); i++ {
43 c := s[i]
44 switch {
45 case c >= 'a' && c <= 'z', c >= '0' && c <= '9':
46 case value && (c == '-' || c == '_' || c == '.'):
47 default:
48 return false
49 }
50 }
51 return true
52}
53
54func joinDN(rdns []rdn) string {
55 parts := make([]string, len(rdns))
56 for i, r := range rdns {
57 parts[i] = r.typ + "=" + r.val
58 }
59 return strings.Join(parts, ",")
60}
61
62func normalizeDN(s string) (string, error) {
63 rdns, err := parseDN(s)
64 if err != nil {
65 return "", err
66 }
67 return joinDN(rdns), nil
68}