Files
glauth/pkg/handler/config.go
T
Jörn Friedrich Dreyer 1b92cfbae6 v2.0.0 RC1 (#125)
Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: Chris F Ravenscroft <chris@voilaweb.com>
Co-authored-by: fanlix <fanlix@gmail.com>
2021-08-12 21:41:58 -07:00

326 lines
10 KiB
Go

package handler
import (
"fmt"
"net"
"regexp"
"sort"
"strings"
"github.com/GeertJohan/yubigo"
"github.com/glauth/glauth/pkg/config"
"github.com/glauth/glauth/pkg/stats"
"github.com/go-logr/logr"
"github.com/nmcclain/ldap"
)
type configHandler struct {
backend config.Backend
log logr.Logger
cfg *config.Config
yubikeyAuth *yubigo.YubiAuth
ldohelper LDAPOpsHelper
attmatcher *regexp.Regexp
}
// NewConfigHandler creates a new config backed handler
func NewConfigHandler(opts ...Option) Handler {
options := newOptions(opts...)
handler := configHandler{
backend: options.Backend,
log: options.Logger,
cfg: options.Config, // TODO only used to access Users and Groups, move that to dedicated options
yubikeyAuth: options.YubiAuth,
ldohelper: options.LDAPHelper,
attmatcher: configattributematcher,
}
return handler
}
func (h configHandler) GetBackend() config.Backend {
return h.backend
}
func (h configHandler) GetLog() logr.Logger {
return h.log
}
func (h configHandler) GetCfg() *config.Config {
return h.cfg
}
func (h configHandler) GetYubikeyAuth() *yubigo.YubiAuth {
return h.yubikeyAuth
}
// Bind implements a bind request against the config file
func (h configHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultCode ldap.LDAPResultCode, err error) {
return h.ldohelper.Bind(h, bindDN, bindSimplePw, conn)
}
// Search implements a search request against the config file
func (h configHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (result ldap.ServerSearchResult, err error) {
return h.ldohelper.Search(h, bindDN, searchReq, conn)
}
// Add is not supported for a static config file
func (h configHandler) Add(boundDN string, req ldap.AddRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
return ldap.LDAPResultInsufficientAccessRights, nil
}
// Modify is not supported for a static config file
func (h configHandler) Modify(boundDN string, req ldap.ModifyRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
return ldap.LDAPResultInsufficientAccessRights, nil
}
// Delete is not supported for a static config file
func (h configHandler) Delete(boundDN string, deleteDN string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
return ldap.LDAPResultInsufficientAccessRights, nil
}
func (h configHandler) FindUser(userName string, searchByUPN bool) (f bool, u config.User, err error) {
user := config.User{}
found := false
for _, u := range h.cfg.Users {
if searchByUPN {
if strings.EqualFold(u.Mail, userName) {
found = true
user = u
}
} else {
if strings.EqualFold(u.Name, userName) {
found = true
user = u
}
}
}
return found, user, nil
}
func (h configHandler) FindGroup(groupName string) (f bool, g config.Group, err error) {
// TODO Does g get erased, and above does u get erased?
// TODO and what about f?
group := config.Group{}
found := false
for _, g := range h.cfg.Groups {
if strings.EqualFold(g.Name, groupName) {
found = true
group = g
}
}
return found, group, nil
}
func (h configHandler) FindPosixAccounts() (entrylist []*ldap.Entry, err error) {
entries := []*ldap.Entry{}
for _, u := range h.cfg.Users {
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{Name: "cn", Values: []string{u.Name}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{u.Name}})
if len(u.GivenName) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: "givenName", Values: []string{u.GivenName}})
}
if len(u.SN) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: "sn", Values: []string{u.SN}})
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "ou", Values: []string{h.getGroupName(u.PrimaryGroup)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "uidNumber", Values: []string{fmt.Sprintf("%d", u.UIDNumber)}})
if u.Disabled {
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"inactive"}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"active"}})
}
if len(u.Mail) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: "mail", Values: []string{u.Mail}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "userPrincipalName", Values: []string{u.Mail}})
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixAccount", "shadowAccount"}})
if len(u.LoginShell) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: "loginShell", Values: []string{u.LoginShell}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{Name: "loginShell", Values: []string{"/bin/bash"}})
}
if len(u.Homedir) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: "homeDirectory", Values: []string{u.Homedir}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{Name: "homeDirectory", Values: []string{"/home/" + u.Name}})
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s", u.Name)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "gecos", Values: []string{fmt.Sprintf("%s", u.Name)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "gidNumber", Values: []string{fmt.Sprintf("%d", u.PrimaryGroup)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberOf", Values: h.getGroupDNs(append(u.OtherGroups, u.PrimaryGroup))})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowExpire", Values: []string{"-1"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowFlag", Values: []string{"134538308"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowInactive", Values: []string{"-1"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowLastChange", Values: []string{"11000"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowMax", Values: []string{"99999"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowMin", Values: []string{"-1"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowWarning", Values: []string{"7"}})
if len(u.SSHKeys) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.SSHKeyAttr, Values: u.SSHKeys})
}
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(u.PrimaryGroup), h.backend.BaseDN)
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
}
return entries, nil
}
func (h configHandler) FindPosixGroups() (entrylist []*ldap.Entry, err error) {
entries := []*ldap.Entry{}
for _, g := range h.cfg.Groups {
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{Name: "cn", Values: []string{g.Name}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{g.Name}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s", g.Name)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "gidNumber", Values: []string{fmt.Sprintf("%d", g.GIDNumber)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixGroup"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "uniqueMember", Values: h.getGroupMembers(g.GIDNumber)})
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberUid", Values: h.getGroupMemberIDs(g.GIDNumber)})
dn := fmt.Sprintf("cn=%s,%s=groups,%s", g.Name, h.backend.GroupFormat, h.backend.BaseDN)
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
}
return entries, nil
}
// Close does not actually close anything, because the config data is kept in memory
func (h configHandler) Close(boundDn string, conn net.Conn) error {
stats.Frontend.Add("closes", 1)
return nil
}
func (h configHandler) getGroupMembers(gid int) []string {
members := make(map[string]bool)
for _, u := range h.cfg.Users {
if u.PrimaryGroup == gid {
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(u.PrimaryGroup), h.backend.BaseDN)
members[dn] = true
} else {
for _, othergid := range u.OtherGroups {
if othergid == gid {
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(u.PrimaryGroup), h.backend.BaseDN)
members[dn] = true
}
}
}
}
for _, g := range h.cfg.Groups {
if gid == g.GIDNumber {
for _, includegroupid := range g.IncludeGroups {
if includegroupid != gid {
includegroupmembers := h.getGroupMembers(includegroupid)
for _, includegroupmember := range includegroupmembers {
members[includegroupmember] = true
}
}
}
}
}
m := []string{}
for k := range members {
m = append(m, k)
}
sort.Strings(m)
return m
}
func (h configHandler) getGroupMemberIDs(gid int) []string {
members := make(map[string]bool)
for _, u := range h.cfg.Users {
if u.PrimaryGroup == gid {
members[u.Name] = true
} else {
for _, othergid := range u.OtherGroups {
if othergid == gid {
members[u.Name] = true
}
}
}
}
for _, g := range h.cfg.Groups {
if gid == g.GIDNumber {
for _, includegroupid := range g.IncludeGroups {
if includegroupid == gid {
h.log.V(2).Info("Ignoring myself as included group", "groupid", includegroupid)
} else {
includegroupmemberids := h.getGroupMemberIDs(includegroupid)
for _, includegroupmemberid := range includegroupmemberids {
members[includegroupmemberid] = true
}
}
}
}
}
m := []string{}
for k := range members {
m = append(m, k)
}
sort.Strings(m)
return m
}
// Converts an array of GUIDs into an array of DNs
func (h configHandler) getGroupDNs(gids []int) []string {
groups := make(map[string]bool)
for _, gid := range gids {
for _, g := range h.cfg.Groups {
if g.GIDNumber == gid {
dn := fmt.Sprintf("cn=%s,%s=groups,%s", g.Name, h.backend.GroupFormat, h.backend.BaseDN)
groups[dn] = true
}
for _, includegroupid := range g.IncludeGroups {
if includegroupid == gid && g.GIDNumber != gid {
includegroupdns := h.getGroupDNs([]int{g.GIDNumber})
for _, includegroupdn := range includegroupdns {
groups[includegroupdn] = true
}
}
}
}
}
g := []string{}
for k := range groups {
g = append(g, k)
}
sort.Strings(g)
return g
}
func (h configHandler) getGroupName(gid int) string {
for _, g := range h.cfg.Groups {
if g.GIDNumber == gid {
return g.Name
}
}
return ""
}