1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2025-12-20 15:07:03 +00:00
Files
go-chasquid-smtp/internal/domaininfo/domaininfo.go
Alberto Bertogli c013c98283 domaininfo: New package to track domain (security) information
This patch introduces a new "domaininfo" package, which implements a
database with information about domains.  In particular, it tracks
incoming and outgoing security levels.

That information is used in incoming and outgoing SMTP to prevent
downgrades.
2016-10-21 22:15:09 +01:00

134 lines
2.9 KiB
Go

// Package domaininfo implements a domain information database, to keep track
// of things we know about a particular domain.
package domaininfo
import (
"fmt"
"sync"
"blitiri.com.ar/go/chasquid/internal/protoio"
"blitiri.com.ar/go/chasquid/internal/trace"
)
// Command to generate domaininfo.pb.go.
//go:generate protoc --go_out=. domaininfo.proto
type DB struct {
// Persistent store with the list of domains we know.
store *protoio.Store
info map[string]*Domain
sync.Mutex
ev *trace.EventLog
}
func New(dir string) (*DB, error) {
st, err := protoio.NewStore(dir)
if err != nil {
return nil, err
}
l := &DB{
store: st,
info: map[string]*Domain{},
}
l.ev = trace.NewEventLog("DomainInfo", fmt.Sprintf("%p", l))
return l, nil
}
// Load the database from disk; should be called once at initialization.
func (db *DB) Load() error {
ids, err := db.store.ListIDs()
if err != nil {
return err
}
for _, id := range ids {
d := &Domain{}
_, err := db.store.Get(id, d)
if err != nil {
return fmt.Errorf("error loading %q: %v", id, err)
}
db.info[d.Name] = d
}
db.ev.Debugf("loaded: %s", ids)
return nil
}
func (db *DB) write(d *Domain) {
err := db.store.Put(d.Name, d)
if err != nil {
db.ev.Errorf("%s error saving: %v", d.Name, err)
} else {
db.ev.Debugf("%s saved", d.Name)
}
}
// IncomingSecLevel checks an incoming security level for the domain.
// Returns true if allowed, false otherwise.
func (db *DB) IncomingSecLevel(domain string, level SecLevel) bool {
db.Lock()
defer db.Unlock()
d, exists := db.info[domain]
if !exists {
d = &Domain{Name: domain}
db.info[domain] = d
defer db.write(d)
}
if level < d.IncomingSecLevel {
db.ev.Errorf("%s incoming denied: %s < %s",
d.Name, level, d.IncomingSecLevel)
return false
} else if level == d.IncomingSecLevel {
db.ev.Debugf("%s incoming allowed: %s == %s",
d.Name, level, d.IncomingSecLevel)
return true
} else {
db.ev.Printf("%s incoming level raised: %s > %s",
d.Name, level, d.IncomingSecLevel)
d.IncomingSecLevel = level
if exists {
defer db.write(d)
}
return true
}
}
// OutgoingSecLevel checks an incoming security level for the domain.
// Returns true if allowed, false otherwise.
func (db *DB) OutgoingSecLevel(domain string, level SecLevel) bool {
db.Lock()
defer db.Unlock()
d, exists := db.info[domain]
if !exists {
d = &Domain{Name: domain}
db.info[domain] = d
defer db.write(d)
}
if level < d.OutgoingSecLevel {
db.ev.Errorf("%s outgoing denied: %s < %s",
d.Name, level, d.OutgoingSecLevel)
return false
} else if level == d.OutgoingSecLevel {
db.ev.Debugf("%s outgoing allowed: %s == %s",
d.Name, level, d.OutgoingSecLevel)
return true
} else {
db.ev.Printf("%s outgoing level raised: %s > %s",
d.Name, level, d.OutgoingSecLevel)
d.OutgoingSecLevel = level
if exists {
defer db.write(d)
}
return true
}
}