package ldap import ( "errors" "strings" ) var errDN = errors.New("ldap: unsupported distinguished name") type rdn struct { typ string val string } func parseDN(s string) ([]rdn, error) { if s == "" { return nil, nil } if strings.ContainsAny(s, "\\\"+#;<>") { return nil, errDN } var out []rdn for _, part := range strings.Split(s, ",") { if strings.Count(part, "=") != 1 { return nil, errDN } typ, val, _ := strings.Cut(part, "=") typ = strings.ToLower(strings.Trim(typ, " ")) val = strings.ToLower(strings.Trim(val, " ")) if !dnToken(typ, false) || !dnToken(val, true) { return nil, errDN } out = append(out, rdn{typ: typ, val: val}) } return out, nil } func dnToken(s string, value bool) bool { if s == "" || len(s) > 64 { return false } for i := 0; i < len(s); i++ { c := s[i] switch { case c >= 'a' && c <= 'z', c >= '0' && c <= '9': case value && (c == '-' || c == '_' || c == '.'): default: return false } } return true } func joinDN(rdns []rdn) string { parts := make([]string, len(rdns)) for i, r := range rdns { parts[i] = r.typ + "=" + r.val } return strings.Join(parts, ",") } func normalizeDN(s string) (string, error) { rdns, err := parseDN(s) if err != nil { return "", err } return joinDN(rdns), nil }