boulder/policy/policy-data.go

142 lines
3.4 KiB
Go

// Copyright 2015 ISRG. All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package policy
import (
"fmt"
"strings"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/sa"
gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1"
)
const whitelisted = "whitelist"
const blacklisted = "blacklist"
// DomainRule ...
type DomainRule struct {
Host string `db:"host"`
Type string `db:"type"`
}
func reverseName(domain string) string {
labels := strings.Split(domain, ".")
for i, j := 0, len(labels)-1; i < j; i, j = i+1, j-1 {
labels[i], labels[j] = labels[j], labels[i]
}
return strings.Join(labels, ".")
}
// PolicyAuthorityDatabaseImpl enforces policy decisions based on various rule
// lists
type PolicyAuthorityDatabaseImpl struct {
log *blog.AuditLogger
dbMap *gorp.DbMap
}
// NewPolicyAuthorityDatabaseImpl constructs a Policy Authority Database (and
// creates tables if they are non-existent)
func NewPolicyAuthorityDatabaseImpl(name string) (padb *PolicyAuthorityDatabaseImpl, err error) {
logger := blog.GetAuditLogger()
dbMap, err := sa.NewDbMap(name)
if err != nil {
return nil, err
}
dbMap.AddTableWithName(DomainRule{}, "ruleList").SetKeys(false, "Host")
err = dbMap.CreateTablesIfNotExists()
if err != nil {
return
}
padb = &PolicyAuthorityDatabaseImpl{
dbMap: dbMap,
log: logger,
}
return padb, nil
}
// LoadRules loads the whitelist and blacklist into the database in a transaction
// deleting any previous content
func (padb *PolicyAuthorityDatabaseImpl) LoadRules(rules []DomainRule) error {
tx, err := padb.dbMap.Begin()
if err != nil {
tx.Rollback()
return err
}
_, err = tx.Exec("DELETE FROM ruleList")
if err != nil {
tx.Rollback()
return err
}
for _, r := range rules {
r.Host = reverseName(r.Host)
tx.Insert(&r)
}
err = tx.Commit()
return err
}
// DumpRules retrieves all DomainRules in the database so they can be written to
// disk
func (padb *PolicyAuthorityDatabaseImpl) DumpRules() ([]DomainRule, error) {
var dR []DomainRule
_, err := padb.dbMap.Select(&dR, "SELECT * FROM ruleList")
for _, r := range dR {
r.Host = reverseName(r.Host)
}
return dR, err
}
// CheckRules will query the database for white/blacklist rules that match host,
// if both whitelist and blacklist rules are found the whitelist will always win
func (padb *PolicyAuthorityDatabaseImpl) CheckRules(host string, requireWhitelisted bool) error {
host = reverseName(host)
var rules []DomainRule
_, err := padb.dbMap.Select(
&rules,
`SELECT * FROM ruleList WHERE :host >= host ORDER BY host ASC`,
map[string]interface{}{"host": host},
)
if err != nil {
return err
}
var wRules []string
var bRules []string
for _, r := range rules {
switch r.Type {
case blacklisted:
if strings.HasPrefix(host, r.Host) {
bRules = append(bRules, r.Host)
}
case whitelisted:
if host == r.Host {
wRules = append(wRules, r.Host)
}
}
}
if requireWhitelisted && len(wRules) == 0 {
return fmt.Errorf("Domain name is not whitelisted for issuance")
} else if len(wRules)+len(bRules) > 0 {
padb.log.Info(fmt.Sprintf("Hostname [%s] matches rules, Whitelist: %s, Blacklist: %s", host, wRules, bRules))
if len(wRules) > 0 {
return nil
}
return BlacklistedError{}
}
return nil
}