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  }