Raw
 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}