Raw
  1package ldap
  2
  3import (
  4	"context"
  5	"crypto/tls"
  6	"errors"
  7	"log/slog"
  8	"net"
  9	"time"
 10
 11	"tug/internal/platform/ratelimit"
 12	"tug/internal/service/refusalcap"
 13	"tug/internal/storage"
 14	fk "tug/internal/transport/ldap/internal/glauthldap"
 15)
 16
 17const (
 18	readTimeout     = 2 * time.Minute
 19	writeTimeout    = 30 * time.Second
 20	maxRequestBytes = 64 << 10
 21)
 22
 23var errNoStore = errors.New("ldap: no store")
 24
 25type Config struct {
 26	BaseDN       string
 27	PrimaryGroup string
 28	TLSCert      string
 29	TLSKey       string
 30	Observe      func(result string)
 31	Refusals     *refusalcap.Cap
 32}
 33
 34type Server struct {
 35	fork    *fk.Server
 36	dir     *directory
 37	tls     *tls.Config
 38	logger  *slog.Logger
 39	backoff *backoff
 40	peers   *peerLimit
 41	observe func(string)
 42	cursors *cursors
 43	binds   *bindWindow
 44	refusal *refusalcap.Cap
 45	ctx     context.Context
 46	cancel  context.CancelFunc
 47}
 48
 49type refuser struct{}
 50
 51func (refuser) Add(string, fk.AddRequest, net.Conn) (fk.LDAPResultCode, error) {
 52	return fk.LDAPResultUnwillingToPerform, nil
 53}
 54
 55func (refuser) Modify(string, fk.ModifyRequest, net.Conn) (fk.LDAPResultCode, error) {
 56	return fk.LDAPResultUnwillingToPerform, nil
 57}
 58
 59func (refuser) Delete(string, string, net.Conn) (fk.LDAPResultCode, error) {
 60	return fk.LDAPResultUnwillingToPerform, nil
 61}
 62
 63func (refuser) ModifyDN(string, fk.ModifyDNRequest, net.Conn) (fk.LDAPResultCode, error) {
 64	return fk.LDAPResultUnwillingToPerform, nil
 65}
 66
 67func (refuser) Compare(string, fk.CompareRequest, net.Conn) (fk.LDAPResultCode, error) {
 68	return fk.LDAPResultUnwillingToPerform, nil
 69}
 70
 71func (refuser) Extended(string, fk.ExtendedRequest, net.Conn) (fk.LDAPResultCode, error) {
 72	return fk.LDAPResultProtocolError, nil
 73}
 74
 75func New(store *storage.Store, cfg Config, logger *slog.Logger) (*Server, error) {
 76	if store == nil {
 77		return nil, errNoStore
 78	}
 79	dir, err := newDirectory(store, cfg.BaseDN, cfg.PrimaryGroup)
 80	if err != nil {
 81		return nil, err
 82	}
 83	var tlsConfig *tls.Config
 84	if cfg.TLSCert != "" || cfg.TLSKey != "" {
 85		pair, err := loadKeyPair(cfg.TLSCert, cfg.TLSKey)
 86		if err != nil {
 87			return nil, err
 88		}
 89		tlsConfig = &tls.Config{Certificates: []tls.Certificate{pair}, MinVersion: tls.VersionTLS12}
 90	}
 91	ctx, cancel := context.WithCancel(context.Background())
 92	s := &Server{
 93		dir:     dir,
 94		tls:     tlsConfig,
 95		logger:  logger,
 96		backoff: newBackoff(time.Now, backoffEntries),
 97		peers:   newPeerLimit(time.Now, ratelimit.Rate{Burst: peerBurst, Every: peerEvery}, peerEntries),
 98		observe: observeOrDiscard(cfg.Observe),
 99		cursors: &cursors{byConn: map[net.Conn][]*cursor{}},
100		binds:   newBindWindow(time.Now, bindWindowEntries),
101		refusal: refusalsOrOwn(cfg.Refusals, store, logger),
102		ctx:     ctx,
103		cancel:  cancel,
104	}
105	f := fk.NewServer()
106	f.ReadTimeout = readTimeout
107	f.WriteTimeout = writeTimeout
108	f.MaxRequestBytes = maxRequestBytes
109	f.BindFunc("", handler{s})
110	f.StructuredSearchFunc("", handler{s})
111	f.AddFunc("", refuser{})
112	f.ModifyFunc("", refuser{})
113	f.DeleteFunc("", refuser{})
114	f.ModifyDNFunc("", refuser{})
115	f.CompareFunc("", refuser{})
116	f.ExtendedFunc("", refuser{})
117	f.CloseFunc("", handler{s})
118	s.fork = f
119	return s, nil
120}
121
122type handler struct{ s *Server }
123
124func (h handler) Bind(bindDN, bindSimplePw string, conn net.Conn) (fk.LDAPResultCode, error) {
125	code, result := h.s.bindFrom(peerAddr(conn), bindDN, bindSimplePw)
126	h.s.observe(result)
127	return code, nil
128}
129
130func (h handler) SearchStructured(boundDN string, req fk.StructuredSearchRequest, conn net.Conn) (fk.ServerSearchResult, error) {
131	return h.s.searchStructured(boundDN, req, conn)
132}
133
134func (h handler) Close(boundDN string, conn net.Conn) error {
135	h.s.cursors.drop(conn)
136	return conn.Close()
137}
138
139func (s *Server) Serve(ln net.Listener) error {
140	if s.tls != nil {
141		ln = tls.NewListener(ln, s.tls)
142	}
143	return s.fork.Serve(ln)
144}
145
146func (s *Server) Close() {
147	s.cancel()
148	s.fork.Close()
149}
150
151func (s *Server) Ready(ctx context.Context) error {
152	_, err := s.dir.store.GroupByName(ctx, s.dir.primaryGroup)
153	if err != nil && !errors.Is(err, storage.ErrNotFound) {
154		return err
155	}
156	return nil
157}
158
159func observeOrDiscard(observe func(string)) func(string) {
160	if observe == nil {
161		return func(string) {}
162	}
163	return observe
164}