maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1/*
  2Maddy Mail Server - Composable all-in-one email server.
  3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
  4
  5This program is free software: you can redistribute it and/or modify
  6it under the terms of the GNU General Public License as published by
  7the Free Software Foundation, either version 3 of the License, or
  8(at your option) any later version.
  9
 10This program is distributed in the hope that it will be useful,
 11but WITHOUT ANY WARRANTY; without even the implied warranty of
 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13GNU General Public License for more details.
 14
 15You should have received a copy of the GNU General Public License
 16along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17*/
 18
 19package dovecotsasld
 20
 21import (
 22	"fmt"
 23	stdlog "log"
 24	"net"
 25	"strings"
 26	"sync"
 27
 28	"github.com/emersion/go-sasl"
 29	dovecotsasl "github.com/foxcpp/go-dovecot-sasl"
 30	"github.com/foxcpp/maddy/framework/config"
 31	modconfig "github.com/foxcpp/maddy/framework/config/module"
 32	"github.com/foxcpp/maddy/framework/log"
 33	"github.com/foxcpp/maddy/framework/module"
 34	"github.com/foxcpp/maddy/internal/auth"
 35	"github.com/foxcpp/maddy/internal/authz"
 36)
 37
 38const modName = "dovecot_sasld"
 39
 40type Endpoint struct {
 41	addrs    []string
 42	log      log.Logger
 43	saslAuth auth.SASLAuth
 44
 45	listenersWg sync.WaitGroup
 46
 47	srv *dovecotsasl.Server
 48}
 49
 50func New(_ string, addrs []string) (module.Module, error) {
 51	return &Endpoint{
 52		addrs: addrs,
 53		saslAuth: auth.SASLAuth{
 54			Log: log.Logger{Name: modName + "/saslauth"},
 55		},
 56		log: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug},
 57	}, nil
 58}
 59
 60func (endp *Endpoint) Name() string {
 61	return modName
 62}
 63
 64func (endp *Endpoint) InstanceName() string {
 65	return modName
 66}
 67
 68func (endp *Endpoint) Init(cfg *config.Map) error {
 69	cfg.Callback("auth", func(m *config.Map, node config.Node) error {
 70		return endp.saslAuth.AddProvider(m, node)
 71	})
 72	cfg.Bool("sasl_login", false, false, &endp.saslAuth.EnableLogin)
 73	config.EnumMapped(cfg, "auth_map_normalize", true, false, authz.NormalizeFuncs, authz.NormalizeAuto,
 74		&endp.saslAuth.AuthNormalize)
 75	modconfig.Table(cfg, "auth_map", true, false, nil, &endp.saslAuth.AuthMap)
 76	if _, err := cfg.Process(); err != nil {
 77		return err
 78	}
 79
 80	endp.srv = dovecotsasl.NewServer()
 81	endp.srv.Log = stdlog.New(endp.log, "", 0)
 82	endp.saslAuth.Log.Debug = endp.log.Debug
 83
 84	for _, mech := range endp.saslAuth.SASLMechanisms() {
 85		endp.srv.AddMechanism(mech, mechInfo[mech], func(req *dovecotsasl.AuthReq) sasl.Server {
 86			var remoteAddr net.Addr
 87			if req.RemoteIP != nil && req.RemotePort != 0 {
 88				remoteAddr = &net.TCPAddr{IP: req.RemoteIP, Port: int(req.RemotePort)}
 89			}
 90
 91			return endp.saslAuth.CreateSASL(mech, remoteAddr, func(_ string, _ auth.ContextData) error { return nil })
 92		})
 93	}
 94
 95	for _, addr := range endp.addrs {
 96		parsed, err := config.ParseEndpoint(addr)
 97		if err != nil {
 98			return fmt.Errorf("%s: %v", modName, err)
 99		}
100
101		l, err := net.Listen(parsed.Network(), parsed.Address())
102		if err != nil {
103			return fmt.Errorf("%s: %v", modName, err)
104		}
105		endp.log.Printf("listening on %v", l.Addr())
106
107		endp.listenersWg.Add(1)
108		go func() {
109			defer endp.listenersWg.Done()
110			if err := endp.srv.Serve(l); err != nil {
111				if !strings.HasSuffix(err.Error(), "use of closed network connection") {
112					endp.log.Printf("failed to serve %v: %v", l.Addr(), err)
113				}
114			}
115		}()
116	}
117
118	return nil
119}
120
121func (endp *Endpoint) Close() error {
122	return endp.srv.Close()
123}
124
125func init() {
126	module.RegisterEndpoint(modName, New)
127}