~ubuntu-branches/debian/sid/skydns/sid

« back to all changes in this revision

Viewing changes to server/dnssec.go

  • Committer: Package Import Robot
  • Author(s): Dmitry Smirnov
  • Date: 2016-03-24 04:30:22 UTC
  • Revision ID: package-import@ubuntu.com-20160324043022-9jtayhol3u8xttzk
Tags: upstream-2.5.3a
ImportĀ upstreamĀ versionĀ 2.5.3a

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2013 Erik St. Martin, Brian Ketelsen. All rights reserved.
 
2
// Use of this source code is governed by The MIT License (MIT) that can be
 
3
// found in the LICENSE file.
 
4
 
 
5
package server
 
6
 
 
7
import (
 
8
        "crypto"
 
9
        "crypto/ecdsa"
 
10
        "crypto/rsa"
 
11
        "os"
 
12
        "time"
 
13
 
 
14
        "github.com/miekg/dns"
 
15
        "github.com/skynetservices/skydns/cache"
 
16
)
 
17
 
 
18
// ParseKeyFile read a DNSSEC keyfile as generated by dnssec-keygen or other
 
19
// utilities. It add ".key" for the public key and ".private" for the private key.
 
20
func ParseKeyFile(file string) (*dns.DNSKEY, crypto.Signer, error) {
 
21
        f, e := os.Open(file + ".key")
 
22
        if e != nil {
 
23
                return nil, nil, e
 
24
        }
 
25
        k, e := dns.ReadRR(f, file+".key")
 
26
        if e != nil {
 
27
                return nil, nil, e
 
28
        }
 
29
        f, e = os.Open(file + ".private")
 
30
        if e != nil {
 
31
                return nil, nil, e
 
32
        }
 
33
        p, e := k.(*dns.DNSKEY).ReadPrivateKey(f, file+".private")
 
34
        if e != nil {
 
35
                return nil, nil, e
 
36
        }
 
37
 
 
38
        if v, ok := p.(*rsa.PrivateKey); ok {
 
39
                return k.(*dns.DNSKEY), v, nil
 
40
        }
 
41
        if v, ok := p.(*ecdsa.PrivateKey); ok {
 
42
                return k.(*dns.DNSKEY), v, nil
 
43
        }
 
44
        return k.(*dns.DNSKEY), nil, nil
 
45
}
 
46
 
 
47
// Sign signs a message m, it takes care of negative or nodata responses as
 
48
// well by synthesising NSEC3 records. It will also cache the signatures, using
 
49
// a hash of the signed data as a key.
 
50
// We also fake the origin TTL in the signature, because we don't want to
 
51
// throw away signatures when services decide to have longer TTL. So we just
 
52
// set the origTTL to 60.
 
53
// TODO(miek): revisit origTTL
 
54
func (s *server) Sign(m *dns.Msg, bufsize uint16) {
 
55
        now := time.Now().UTC()
 
56
        incep := uint32(now.Add(-3 * time.Hour).Unix())     // 2+1 hours, be sure to catch daylight saving time and such
 
57
        expir := uint32(now.Add(7 * 24 * time.Hour).Unix()) // sign for a week
 
58
 
 
59
        for _, r := range rrSets(m.Answer) {
 
60
                if r[0].Header().Rrtype == dns.TypeRRSIG {
 
61
                        continue
 
62
                }
 
63
                if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
 
64
                        continue
 
65
                }
 
66
                if sig, err := s.signSet(r, now, incep, expir); err == nil {
 
67
                        m.Answer = append(m.Answer, sig)
 
68
                }
 
69
        }
 
70
        for _, r := range rrSets(m.Ns) {
 
71
                if r[0].Header().Rrtype == dns.TypeRRSIG {
 
72
                        continue
 
73
                }
 
74
                if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
 
75
                        continue
 
76
                }
 
77
                if sig, err := s.signSet(r, now, incep, expir); err == nil {
 
78
                        m.Ns = append(m.Ns, sig)
 
79
                }
 
80
        }
 
81
        for _, r := range rrSets(m.Extra) {
 
82
                if r[0].Header().Rrtype == dns.TypeRRSIG || r[0].Header().Rrtype == dns.TypeOPT {
 
83
                        continue
 
84
                }
 
85
                if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
 
86
                        continue
 
87
                }
 
88
                if sig, err := s.signSet(r, now, incep, expir); err == nil {
 
89
                        m.Extra = append(m.Extra, sig)
 
90
                }
 
91
        }
 
92
 
 
93
        o := new(dns.OPT)
 
94
        o.Hdr.Name = "."
 
95
        o.Hdr.Rrtype = dns.TypeOPT
 
96
        o.SetDo()
 
97
        o.SetUDPSize(4096) // TODO(miek): echo client
 
98
        m.Extra = append(m.Extra, o)
 
99
        return
 
100
}
 
101
 
 
102
func (s *server) signSet(r []dns.RR, now time.Time, incep, expir uint32) (*dns.RRSIG, error) {
 
103
        key := cache.KeyRRset(r)
 
104
        if m, exp, hit := s.scache.Search(key); hit { // There can only be one sig in this cache.
 
105
                // Is it still valid 24 hours from now?
 
106
                if now.Add(+24*time.Hour).Sub(exp) < -24*time.Hour {
 
107
                        return m.Answer[0].(*dns.RRSIG), nil
 
108
                }
 
109
                s.scache.Remove(key)
 
110
        }
 
111
        if s.config.Verbose {
 
112
                logf("scache miss for %s type %d", r[0].Header().Name, r[0].Header().Rrtype)
 
113
        }
 
114
 
 
115
        StatsDnssecCacheMiss.Inc(1)
 
116
        promCacheMiss.WithLabelValues("signature").Inc()
 
117
 
 
118
        sig, err, shared := inflight.Do(key, func() (*dns.RRSIG, error) {
 
119
                sig1 := s.NewRRSIG(incep, expir)
 
120
                sig1.Header().Ttl = r[0].Header().Ttl
 
121
                if r[0].Header().Rrtype == dns.TypeTXT {
 
122
                        sig1.OrigTtl = 0
 
123
                }
 
124
                e := sig1.Sign(s.config.PrivKey, r)
 
125
                if e != nil {
 
126
                        logf("failed to sign: %s", e.Error())
 
127
                }
 
128
                return sig1, e
 
129
        })
 
130
        if err != nil {
 
131
                return nil, err
 
132
        }
 
133
        if !shared {
 
134
                s.scache.InsertSignature(key, sig)
 
135
        }
 
136
        return dns.Copy(sig).(*dns.RRSIG), nil
 
137
}
 
138
 
 
139
func (s *server) NewRRSIG(incep, expir uint32) *dns.RRSIG {
 
140
        sig := new(dns.RRSIG)
 
141
        sig.Hdr.Rrtype = dns.TypeRRSIG
 
142
        sig.Hdr.Ttl = s.config.Ttl
 
143
        sig.OrigTtl = s.config.Ttl
 
144
        sig.Algorithm = s.config.PubKey.Algorithm
 
145
        sig.KeyTag = s.config.KeyTag
 
146
        sig.Inception = incep
 
147
        sig.Expiration = expir
 
148
        sig.SignerName = s.config.PubKey.Hdr.Name
 
149
        return sig
 
150
}
 
151
 
 
152
type rrset struct {
 
153
        qname string
 
154
        qtype uint16
 
155
}
 
156
 
 
157
func rrSets(rrs []dns.RR) map[rrset][]dns.RR {
 
158
        m := make(map[rrset][]dns.RR)
 
159
        for _, r := range rrs {
 
160
                if s, ok := m[rrset{r.Header().Name, r.Header().Rrtype}]; ok {
 
161
                        s = append(s, r)
 
162
                        m[rrset{r.Header().Name, r.Header().Rrtype}] = s
 
163
                } else {
 
164
                        s := make([]dns.RR, 1, 3)
 
165
                        s[0] = r
 
166
                        m[rrset{r.Header().Name, r.Header().Rrtype}] = s
 
167
                }
 
168
        }
 
169
        if len(m) > 0 {
 
170
                return m
 
171
        }
 
172
        return nil
 
173
}