diff --git a/builtin/credential/ldap/path_config.go b/builtin/credential/ldap/path_config.go index f2776b1a0366..e426ca3bbb84 100644 --- a/builtin/credential/ldap/path_config.go +++ b/builtin/credential/ldap/path_config.go @@ -50,6 +50,9 @@ func (b *backend) Config(ctx context.Context, req *logical.Request) (*ldaputil.C result.CaseSensitiveNames = new(bool) *result.CaseSensitiveNames = false + result.UsePre111GroupCNBehavior = new(bool) + *result.UsePre111GroupCNBehavior = false + return result, nil } @@ -67,6 +70,12 @@ func (b *backend) Config(ctx context.Context, req *logical.Request) (*ldaputil.C persistNeeded = true } + if result.UsePre111GroupCNBehavior == nil { + result.UsePre111GroupCNBehavior = new(bool) + *result.UsePre111GroupCNBehavior = true + persistNeeded = true + } + if persistNeeded && (b.System().LocalMount() || !b.System().ReplicationState().HasState(consts.ReplicationPerformanceSecondary|consts.ReplicationPerformanceStandby)) { entry, err := logical.StorageEntryJSON("config", result) if err != nil { @@ -109,6 +118,11 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, d * *cfg.CaseSensitiveNames = false } + if cfg.UsePre111GroupCNBehavior == nil { + cfg.UsePre111GroupCNBehavior = new(bool) + *cfg.UsePre111GroupCNBehavior = false + } + entry, err := logical.StorageEntryJSON("config", cfg) if err != nil { return nil, err diff --git a/helper/ldaputil/client.go b/helper/ldaputil/client.go index f117c6060687..2ba724929b4c 100644 --- a/helper/ldaputil/client.go +++ b/helper/ldaputil/client.go @@ -357,12 +357,12 @@ func (c *Client) GetLdapGroups(cfg *ConfigEntry, conn Connection, userDN string, values := e.GetAttributeValues(cfg.GroupAttr) if len(values) > 0 { for _, val := range values { - groupCN := getCN(val) + groupCN := getCN(cfg, val) ldapMap[groupCN] = true } } else { // If groupattr didn't resolve, use self (enumerating group objects) - groupCN := getCN(e.DN) + groupCN := getCN(cfg, e.DN) ldapMap[groupCN] = true } } @@ -419,7 +419,7 @@ func EscapeLDAPValue(input string) string { * Given a non-conforming string (such as an already-extracted CN), * it will be returned as-is. */ -func getCN(dn string) string { +func getCN(cfg *ConfigEntry, dn string) string { parsedDN, err := ldap.ParseDN(dn) if err != nil || len(parsedDN.RDNs) == 0 { // It was already a CN, return as-is @@ -428,8 +428,14 @@ func getCN(dn string) string { for _, rdn := range parsedDN.RDNs { for _, rdnAttr := range rdn.Attributes { - if strings.EqualFold(rdnAttr.Type, "CN") { - return rdnAttr.Value + if cfg.UsePre111GroupCNBehavior == nil || *cfg.UsePre111GroupCNBehavior { + if rdnAttr.Type == "CN" { + return rdnAttr.Value + } + } else { + if strings.EqualFold(rdnAttr.Type, "CN") { + return rdnAttr.Value + } } } } diff --git a/helper/ldaputil/config.go b/helper/ldaputil/config.go index 7169a8c82b4a..46341b912d37 100644 --- a/helper/ldaputil/config.go +++ b/helper/ldaputil/config.go @@ -136,6 +136,11 @@ Default: cn`, Default: false, Description: "If true, use the Active Directory tokenGroups constructed attribute of the user to find the group memberships. This will find all security groups including nested ones.", }, + + "use_pre111_group_cn_behavior": { + Type: framework.TypeBool, + Description: "In Vault 1.1.1 a fix for handling group CN values of different cases unfortunately introduced a regression that could cause previously defined groups to not be found due to a change in the resulting name. If set true, the pre-1.1.1 behavior for matching group CNs will be used. This is only needed in some upgrade scenarios for backwards compatibility. It is enabled by default if the config is upgraded but disabled by default on new configurations.", + }, } } @@ -252,6 +257,12 @@ func NewConfigEntry(d *framework.FieldData) (*ConfigEntry, error) { *cfg.CaseSensitiveNames = caseSensitiveNames.(bool) } + usePre111GroupCNBehavior, ok := d.GetOk("use_pre111_group_cn_behavior") + if ok { + cfg.UsePre111GroupCNBehavior = new(bool) + *cfg.UsePre111GroupCNBehavior = usePre111GroupCNBehavior.(bool) + } + useTokenGroups := d.Get("use_token_groups").(bool) if useTokenGroups { cfg.UseTokenGroups = useTokenGroups @@ -261,23 +272,24 @@ func NewConfigEntry(d *framework.FieldData) (*ConfigEntry, error) { } type ConfigEntry struct { - Url string `json:"url"` - UserDN string `json:"userdn"` - GroupDN string `json:"groupdn"` - GroupFilter string `json:"groupfilter"` - GroupAttr string `json:"groupattr"` - UPNDomain string `json:"upndomain"` - UserAttr string `json:"userattr"` - Certificate string `json:"certificate"` - InsecureTLS bool `json:"insecure_tls"` - StartTLS bool `json:"starttls"` - BindDN string `json:"binddn"` - BindPassword string `json:"bindpass"` - DenyNullBind bool `json:"deny_null_bind"` - DiscoverDN bool `json:"discoverdn"` - TLSMinVersion string `json:"tls_min_version"` - TLSMaxVersion string `json:"tls_max_version"` - UseTokenGroups bool `json:"use_token_groups"` + Url string `json:"url"` + UserDN string `json:"userdn"` + GroupDN string `json:"groupdn"` + GroupFilter string `json:"groupfilter"` + GroupAttr string `json:"groupattr"` + UPNDomain string `json:"upndomain"` + UserAttr string `json:"userattr"` + Certificate string `json:"certificate"` + InsecureTLS bool `json:"insecure_tls"` + StartTLS bool `json:"starttls"` + BindDN string `json:"binddn"` + BindPassword string `json:"bindpass"` + DenyNullBind bool `json:"deny_null_bind"` + DiscoverDN bool `json:"discoverdn"` + TLSMinVersion string `json:"tls_min_version"` + TLSMaxVersion string `json:"tls_max_version"` + UseTokenGroups bool `json:"use_token_groups"` + UsePre111GroupCNBehavior *bool `json:"use_pre111_group_cn_behavior"` // This json tag deviates from snake case because there was a past issue // where the tag was being ignored, causing it to be jsonified as "CaseSensitiveNames". @@ -314,6 +326,9 @@ func (c *ConfigEntry) PasswordlessMap() map[string]interface{} { if c.CaseSensitiveNames != nil { m["case_sensitive_names"] = *c.CaseSensitiveNames } + if c.UsePre111GroupCNBehavior != nil { + m["use_pre111_group_cn_behavior"] = *c.UsePre111GroupCNBehavior + } return m }