1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2026-01-27 20:45:56 +00:00

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.
This commit is contained in:
Alberto Bertogli
2016-10-13 02:28:30 +01:00
parent 1d7a207e00
commit c013c98283
8 changed files with 545 additions and 11 deletions

View File

@@ -0,0 +1,133 @@
// 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
}
}