Merge branch 'master' into mailer
This commit is contained in:
commit
bd9286dd5b
|
|
@ -12,51 +12,51 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/auth",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/config",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs11key",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs12",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs7",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/csr",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/errors",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/helpers",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/info",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/log",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/ocsp",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/signer",
|
||||
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
|
||||
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/codegangsta/cli",
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/miekg/dns",
|
||||
"Rev": "6da0cd2c927d5cb11255c468b5c3a1744c3351b1"
|
||||
"Rev": "259969e797348d20e8c144a7573c23f06fa962f5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/miekg/pkcs11",
|
||||
|
|
|
|||
|
|
@ -391,6 +391,7 @@ var KeyUsage = map[string]x509.KeyUsage{
|
|||
"digital signature": x509.KeyUsageDigitalSignature,
|
||||
"content committment": x509.KeyUsageContentCommitment,
|
||||
"key encipherment": x509.KeyUsageKeyEncipherment,
|
||||
"key agreement": x509.KeyUsageKeyAgreement,
|
||||
"data encipherment": x509.KeyUsageDataEncipherment,
|
||||
"cert sign": x509.KeyUsageCertSign,
|
||||
"crl sign": x509.KeyUsageCRLSign,
|
||||
|
|
|
|||
7
Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key/pkcs11key.go
generated
vendored
7
Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key/pkcs11key.go
generated
vendored
|
|
@ -1,4 +1,4 @@
|
|||
// +build pkcs11
|
||||
// +build !nopkcs11
|
||||
|
||||
// Package pkcs11key implements crypto.Signer for PKCS #11 private
|
||||
// keys. Currently, only RSA keys are support.
|
||||
|
|
@ -131,7 +131,10 @@ func New(module, slot, pin, privLabel string) (ps *PKCS11Key, err error) {
|
|||
ps.Destroy()
|
||||
return
|
||||
}
|
||||
ps.publicKey = rsa.PublicKey{n, e}
|
||||
ps.publicKey = rsa.PublicKey{
|
||||
N: n,
|
||||
E: e,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
// Package pkcs11 in the ocsp directory provides a way to construct a
|
||||
// PKCS#11-based OCSP signer. It is only available in binaries built with the
|
||||
// pkcs11 tag, i.e. `go build -tags pkcs11 ./cmd/cfssl`.
|
||||
// +build pkcs11
|
||||
// +build !nopkcs11
|
||||
|
||||
// Package pkcs11 in the ocsp directory provides a way to construct a
|
||||
// PKCS#11-based OCSP signer.
|
||||
package pkcs11
|
||||
|
||||
import (
|
||||
|
|
@ -18,7 +17,7 @@ import (
|
|||
// Enabled is set to true if PKCS #11 support is present.
|
||||
const Enabled = true
|
||||
|
||||
// New returns a new PKCS #11 signer.
|
||||
// NewPKCS11Signer returns a new PKCS #11 signer.
|
||||
func NewPKCS11Signer(cfg ocspConfig.Config) (ocsp.Signer, error) {
|
||||
log.Debugf("Loading PKCS #11 module %s", cfg.PKCS11.Module)
|
||||
certData, err := ioutil.ReadFile(cfg.CACertFile)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// +build !pkcs11
|
||||
// +build nopkcs11
|
||||
|
||||
package pkcs11
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// +build pkcs11
|
||||
// +build !nopkcs11
|
||||
|
||||
package pkcs11
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// +build !pkcs11
|
||||
// +build nopkcs11
|
||||
|
||||
package pkcs11
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ A not-so-up-to-date-list-that-may-be-actually-current:
|
|||
* https://github.com/StalkR/dns-reverse-proxy
|
||||
* https://github.com/tianon/rawdns
|
||||
* https://mesosphere.github.io/mesos-dns/
|
||||
* https://pulse.turbobytes.com/
|
||||
|
||||
Send pull request if you want to be listed here.
|
||||
|
||||
|
|
|
|||
|
|
@ -189,26 +189,15 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
|
|||
// If the received message contains a TSIG record the transaction
|
||||
// signature is verified.
|
||||
func (co *Conn) ReadMsg() (*Msg, error) {
|
||||
var p []byte
|
||||
m := new(Msg)
|
||||
if _, ok := co.Conn.(*net.TCPConn); ok {
|
||||
p = make([]byte, MaxMsgSize)
|
||||
} else {
|
||||
if co.UDPSize > MinMsgSize {
|
||||
p = make([]byte, co.UDPSize)
|
||||
} else {
|
||||
p = make([]byte, MinMsgSize)
|
||||
}
|
||||
}
|
||||
n, err := co.Read(p)
|
||||
if err != nil && n == 0 {
|
||||
p, err := co.ReadMsgHeader(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p = p[:n]
|
||||
|
||||
m := new(Msg)
|
||||
if err := m.Unpack(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
co.rtt = time.Since(co.t)
|
||||
if t := m.IsTsig(); t != nil {
|
||||
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
||||
return m, ErrSecret
|
||||
|
|
@ -219,6 +208,81 @@ func (co *Conn) ReadMsg() (*Msg, error) {
|
|||
return m, err
|
||||
}
|
||||
|
||||
// ReadMsgHeader reads a DNS message, parses and populates hdr (when hdr is not nil).
|
||||
// Returns message as a byte slice to be parsed with Msg.Unpack later on.
|
||||
// Note that error handling on the message body is not possible as only the header is parsed.
|
||||
func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
|
||||
var (
|
||||
p []byte
|
||||
n int
|
||||
err error
|
||||
)
|
||||
|
||||
if t, ok := co.Conn.(*net.TCPConn); ok {
|
||||
// First two bytes specify the length of the entire message.
|
||||
l, err := tcpMsgLen(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p = make([]byte, l)
|
||||
n, err = tcpRead(t, p)
|
||||
} else {
|
||||
if co.UDPSize > MinMsgSize {
|
||||
p = make([]byte, co.UDPSize)
|
||||
} else {
|
||||
p = make([]byte, MinMsgSize)
|
||||
}
|
||||
n, err = co.Read(p)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if n < headerSize {
|
||||
return nil, ErrShortRead
|
||||
}
|
||||
|
||||
p = p[:n]
|
||||
if hdr != nil {
|
||||
if _, err = UnpackStruct(hdr, p, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return p, err
|
||||
}
|
||||
|
||||
// tcpMsgLen is a helper func to read first two bytes of stream as uint16 packet length.
|
||||
func tcpMsgLen(t *net.TCPConn) (int, error) {
|
||||
p := []byte{0, 0}
|
||||
n, err := t.Read(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if n != 2 {
|
||||
return 0, ErrShortRead
|
||||
}
|
||||
l, _ := unpackUint16(p, 0)
|
||||
if l == 0 {
|
||||
return 0, ErrShortRead
|
||||
}
|
||||
return int(l), nil
|
||||
}
|
||||
|
||||
// tcpRead calls TCPConn.Read enough times to fill allocated buffer.
|
||||
func tcpRead(t *net.TCPConn, p []byte) (int, error) {
|
||||
n, err := t.Read(p)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
for n < len(p) {
|
||||
j, err := t.Read(p[n:])
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
n += j
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Read implements the net.Conn read method.
|
||||
func (co *Conn) Read(p []byte) (n int, err error) {
|
||||
if co.Conn == nil {
|
||||
|
|
@ -228,37 +292,22 @@ func (co *Conn) Read(p []byte) (n int, err error) {
|
|||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
if t, ok := co.Conn.(*net.TCPConn); ok {
|
||||
n, err = t.Read(p[0:2])
|
||||
if err != nil || n != 2 {
|
||||
return n, err
|
||||
l, err := tcpMsgLen(t)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
l, _ := unpackUint16(p[0:2], 0)
|
||||
if l == 0 {
|
||||
return 0, ErrShortRead
|
||||
}
|
||||
if int(l) > len(p) {
|
||||
if l > len(p) {
|
||||
return int(l), io.ErrShortBuffer
|
||||
}
|
||||
n, err = t.Read(p[:l])
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
i := n
|
||||
for i < int(l) {
|
||||
j, err := t.Read(p[i:int(l)])
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
i += j
|
||||
}
|
||||
n = i
|
||||
return n, err
|
||||
return tcpRead(t, p[:l])
|
||||
}
|
||||
// UDP connection
|
||||
n, err = co.Conn.Read(p)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
co.rtt = time.Since(co.t)
|
||||
return n, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ func TestClientSync(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("failed to exchange: %v", err)
|
||||
}
|
||||
if r != nil && r.Rcode != RcodeSuccess {
|
||||
if r == nil || r.Rcode != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
}
|
||||
|
|
@ -235,3 +235,52 @@ func ExampleUpdateLeaseTSIG(t *testing.T) {
|
|||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientConn(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
defer HandleRemove("miek.nl.")
|
||||
|
||||
// This uses TCP just to make it slightly different than TestClientSync
|
||||
s, addrstr, err := RunLocalTCPServer("127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeSOA)
|
||||
|
||||
cn, err := Dial("tcp", addrstr)
|
||||
if err != nil {
|
||||
t.Errorf("failed to dial %s: %v", addrstr, err)
|
||||
}
|
||||
|
||||
err = cn.WriteMsg(m)
|
||||
if err != nil {
|
||||
t.Errorf("failed to exchange: %v", err)
|
||||
}
|
||||
r, err := cn.ReadMsg()
|
||||
if r == nil || r.Rcode != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
|
||||
err = cn.WriteMsg(m)
|
||||
if err != nil {
|
||||
t.Errorf("failed to exchange: %v", err)
|
||||
}
|
||||
h := new(Header)
|
||||
buf, err := cn.ReadMsgHeader(h)
|
||||
if buf == nil {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
if int(h.Bits&0xF) != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer in ReadMsgHeader\n%v", r)
|
||||
}
|
||||
if h.Ancount != 0 || h.Qdcount != 1 || h.Nscount != 0 || h.Arcount != 1 {
|
||||
t.Errorf("expected to have question and additional in response; got something else: %+v", h)
|
||||
}
|
||||
if err = r.Unpack(buf); err != nil {
|
||||
t.Errorf("unable to unpack message fully: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,6 +202,9 @@ RFC 6895 sets aside a range of type codes for private use. This range
|
|||
is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these
|
||||
can be used, before requesting an official type code from IANA.
|
||||
|
||||
see http://miek.nl/posts/2014/Sep/21/Private%20RRs%20and%20IDN%20in%20Go%20DNS/ for more
|
||||
information.
|
||||
|
||||
EDNS0
|
||||
|
||||
EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -3,9 +3,10 @@ package idn
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Implementation idea from RFC itself and from from IDNA::Punycode created by
|
||||
|
|
@ -26,8 +27,8 @@ const (
|
|||
)
|
||||
|
||||
// ToPunycode converts unicode domain names to DNS-appropriate punycode names.
|
||||
// This function would return incorrect result for strings for non-canonical
|
||||
// unicode strings.
|
||||
// This function would return an empty string result for domain names with
|
||||
// invalid unicode strings. This function expects domain names in lowercase.
|
||||
func ToPunycode(s string) string {
|
||||
tokens := dns.SplitDomainName(s)
|
||||
switch {
|
||||
|
|
@ -40,7 +41,11 @@ func ToPunycode(s string) string {
|
|||
}
|
||||
|
||||
for i := range tokens {
|
||||
tokens[i] = string(encode([]byte(tokens[i])))
|
||||
t := encode([]byte(tokens[i]))
|
||||
if t == nil {
|
||||
return ""
|
||||
}
|
||||
tokens[i] = string(t)
|
||||
}
|
||||
return strings.Join(tokens, ".")
|
||||
}
|
||||
|
|
@ -138,12 +143,18 @@ func tfunc(k, bias rune) rune {
|
|||
return k - bias
|
||||
}
|
||||
|
||||
// encode transforms Unicode input bytes (that represent DNS label) into punycode bytestream
|
||||
// encode transforms Unicode input bytes (that represent DNS label) into
|
||||
// punycode bytestream. This function would return nil if there's an invalid
|
||||
// character in the label.
|
||||
func encode(input []byte) []byte {
|
||||
n, bias := _N, _BIAS
|
||||
|
||||
b := bytes.Runes(input)
|
||||
for i := range b {
|
||||
if !isValidRune(b[i]) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b[i] = preprune(b[i])
|
||||
}
|
||||
|
||||
|
|
@ -267,3 +278,34 @@ func decode(b []byte) []byte {
|
|||
}
|
||||
return ret.Bytes()
|
||||
}
|
||||
|
||||
// isValidRune checks if the character is valid. We will look for the
|
||||
// character property in the code points list. For now we aren't checking special
|
||||
// rules in case of contextual property
|
||||
func isValidRune(r rune) bool {
|
||||
return findProperty(r) == propertyPVALID
|
||||
}
|
||||
|
||||
// findProperty will try to check the code point property of the given
|
||||
// character. It will use a binary search algorithm as we have a slice of
|
||||
// ordered ranges (average case performance O(log n))
|
||||
func findProperty(r rune) property {
|
||||
imin, imax := 0, len(codePoints)
|
||||
|
||||
for imax >= imin {
|
||||
imid := (imin + imax) / 2
|
||||
|
||||
codePoint := codePoints[imid]
|
||||
if (codePoint.start == r && codePoint.end == 0) || (codePoint.start <= r && codePoint.end >= r) {
|
||||
return codePoint.state
|
||||
}
|
||||
|
||||
if (codePoint.end > 0 && codePoint.end < r) || (codePoint.end == 0 && codePoint.start < r) {
|
||||
imin = imid + 1
|
||||
} else {
|
||||
imax = imid - 1
|
||||
}
|
||||
}
|
||||
|
||||
return propertyUnknown
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ var testcases = [][2]string{
|
|||
{"AbC", "abc"},
|
||||
{"я", "xn--41a"},
|
||||
{"zя", "xn--z-0ub"},
|
||||
{"ЯZ", "xn--z-zub"},
|
||||
{"яZ", "xn--z-zub"},
|
||||
{"а-я", "xn----7sb8g"},
|
||||
{"إختبار", "xn--kgbechtv"},
|
||||
{"آزمایشی", "xn--hgbk6aj7f53bba"},
|
||||
{"测试", "xn--0zwm56d"},
|
||||
{"測試", "xn--g6w251d"},
|
||||
{"Испытание", "xn--80akhbyknj4f"},
|
||||
{"испытание", "xn--80akhbyknj4f"},
|
||||
{"परीक्षा", "xn--11b5bs3a9aj6g"},
|
||||
{"δοκιμή", "xn--jxalpdlp"},
|
||||
{"테스트", "xn--9t4b11yi5a"},
|
||||
|
|
@ -27,6 +27,7 @@ var testcases = [][2]string{
|
|||
{"テスト", "xn--zckzah"},
|
||||
{"பரிட்சை", "xn--hlcj6aya9esc7a"},
|
||||
{"mamão-com-açúcar", "xn--mamo-com-acar-yeb1e6q"},
|
||||
{"σ", "xn--4xa"},
|
||||
}
|
||||
|
||||
func TestEncodeDecodePunycode(t *testing.T) {
|
||||
|
|
@ -81,17 +82,34 @@ func TestEncodeDecodeFinalPeriod(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
var invalid = []string{
|
||||
var invalidACEs = []string{
|
||||
"xn--*",
|
||||
"xn--",
|
||||
"xn---",
|
||||
}
|
||||
|
||||
func TestInvalidPunycode(t *testing.T) {
|
||||
for _, d := range invalid {
|
||||
for _, d := range invalidACEs {
|
||||
s := FromPunycode(d)
|
||||
if s != d {
|
||||
t.Errorf("Changed invalid name %s to %#v", d, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// You can verify the labels that are valid or not comparing to the Verisign
|
||||
// website: http://mct.verisign-grs.com/
|
||||
var invalidUnicodes = []string{
|
||||
"Σ",
|
||||
"ЯZ",
|
||||
"Испытание",
|
||||
}
|
||||
|
||||
func TestInvalidUnicodes(t *testing.T) {
|
||||
for _, d := range invalidUnicodes {
|
||||
s := ToPunycode(d)
|
||||
if s != "" {
|
||||
t.Errorf("Changed invalid name %s to %#v", d, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -921,7 +921,7 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str
|
|||
copy(msg[off:off+len(s)], s)
|
||||
off += len(s)
|
||||
case `dns:"octet"`:
|
||||
bytesTmp := make([]byte, 256*4+1)
|
||||
bytesTmp := make([]byte, 256)
|
||||
off, err = packOctetString(fv.String(), msg, off, bytesTmp)
|
||||
if err != nil {
|
||||
return lenmsg, err
|
||||
|
|
|
|||
|
|
@ -1477,3 +1477,32 @@ func TestParseCAA(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackCAA(t *testing.T) {
|
||||
m := new(Msg)
|
||||
record := new(CAA)
|
||||
record.Hdr = RR_Header{Name: "example.com.", Rrtype: TypeCAA, Class: ClassINET, Ttl: 0}
|
||||
record.Tag = "issue"
|
||||
record.Value = "symantec.com"
|
||||
record.Flag = 1
|
||||
|
||||
m.Answer = append(m.Answer, record)
|
||||
bytes, err := m.Pack()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pack msg: %v", err)
|
||||
}
|
||||
if err := m.Unpack(bytes); err != nil {
|
||||
t.Fatalf("failed to unpack msg: %v", err)
|
||||
}
|
||||
if len(m.Answer) != 1 {
|
||||
t.Fatalf("incorrect number of answers unpacked")
|
||||
}
|
||||
rr := m.Answer[0].(*CAA)
|
||||
if rr.Tag != "issue" {
|
||||
t.Fatalf("invalid tag for unpacked answer")
|
||||
} else if rr.Value != "symantec.com" {
|
||||
t.Fatalf("invalid value for unpacked answer")
|
||||
} else if rr.Flag != 1 {
|
||||
t.Fatalf("invalid flag for unpacked answer")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,6 +158,8 @@ type Header struct {
|
|||
}
|
||||
|
||||
const (
|
||||
headerSize = 12
|
||||
|
||||
// Header.Bits
|
||||
_QR = 1 << 15 // query/response (response=1)
|
||||
_AA = 1 << 10 // authoritative
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ func main() {
|
|||
rai := ra.NewRegistrationAuthorityImpl()
|
||||
rai.AuthzBase = c.Common.BaseURL + wfe.AuthzPath
|
||||
rai.MaxKeySize = c.Common.MaxKeySize
|
||||
raDNSTimeout, err := time.ParseDuration(c.RA.DNSTimeout)
|
||||
raDNSTimeout, err := time.ParseDuration(c.Common.DNSTimeout)
|
||||
cmd.FailOnError(err, "Couldn't parse RA DNS timeout")
|
||||
rai.DNSResolver = core.NewDNSResolverImpl(raDNSTimeout, []string{c.RA.DNSResolver})
|
||||
rai.DNSResolver = core.NewDNSResolverImpl(raDNSTimeout, []string{c.Common.DNSResolver})
|
||||
|
||||
go cmd.ProfileCmd("RA", stats)
|
||||
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ func main() {
|
|||
go cmd.ProfileCmd("VA", stats)
|
||||
|
||||
vai := va.NewValidationAuthorityImpl(c.CA.TestMode)
|
||||
dnsTimeout, err := time.ParseDuration(c.VA.DNSTimeout)
|
||||
dnsTimeout, err := time.ParseDuration(c.Common.DNSTimeout)
|
||||
cmd.FailOnError(err, "Couldn't parse DNS timeout")
|
||||
vai.DNSResolver = core.NewDNSResolverImpl(dnsTimeout, []string{c.VA.DNSResolver})
|
||||
vai.DNSResolver = core.NewDNSResolverImpl(dnsTimeout, []string{c.Common.DNSResolver})
|
||||
vai.UserAgent = c.VA.UserAgent
|
||||
|
||||
for {
|
||||
|
|
|
|||
|
|
@ -91,15 +91,16 @@ func main() {
|
|||
wfei.IssuerCacheDuration, err = time.ParseDuration(c.WFE.IssuerCacheDuration)
|
||||
cmd.FailOnError(err, "Couldn't parse issuer caching duration")
|
||||
|
||||
dnsTimeout, err := time.ParseDuration(c.Common.DNSTimeout)
|
||||
cmd.FailOnError(err, "Couldn't parse DNS timeout")
|
||||
dnsResolver := core.NewDNSResolverImpl(dnsTimeout, []string{c.Common.DNSResolver})
|
||||
|
||||
ra := ra.NewRegistrationAuthorityImpl()
|
||||
raDNSTimeout, err := time.ParseDuration(c.RA.DNSTimeout)
|
||||
cmd.FailOnError(err, "Couldn't parse RA DNS timeout")
|
||||
ra.DNSResolver = core.NewDNSResolverImpl(raDNSTimeout, []string{c.RA.DNSResolver})
|
||||
ra.DNSResolver = dnsResolver
|
||||
|
||||
va := va.NewValidationAuthorityImpl(c.CA.TestMode)
|
||||
vaDNSTimeout, err := time.ParseDuration(c.VA.DNSTimeout)
|
||||
cmd.FailOnError(err, "Couldn't parse VA DNS timeout")
|
||||
va.DNSResolver = core.NewDNSResolverImpl(vaDNSTimeout, []string{c.VA.DNSResolver})
|
||||
va.DNSResolver = dnsResolver
|
||||
va.UserAgent = c.VA.UserAgent
|
||||
|
||||
cadb, err := ca.NewCertificateAuthorityDatabaseImpl(c.CA.DBDriver, c.CA.DBConnect)
|
||||
|
|
|
|||
|
|
@ -1,94 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
// "github.com/cloudflare/cfssl/crypto/pkcs11key"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||
)
|
||||
|
||||
var certFile = flag.String("ca", "", "JSON file for subject and validity")
|
||||
var listFile = flag.String("revoked", "", "JSON file with a list of pkix.RevokedCertificate objects")
|
||||
var module = flag.String("pkcs11-module", "", "PKCS#11 module")
|
||||
var pin = flag.String("pkcs11-pin", "", "PKCS#11 password")
|
||||
var token = flag.String("pkcs11-token", "", "PKCS#11 token name")
|
||||
var label = flag.String("pkcs11-label", "", "PKCS#11 key label")
|
||||
|
||||
// Config defines the configuration loaded from listFile.
|
||||
type Config struct {
|
||||
ThisUpdate time.Time
|
||||
NextUpdate time.Time
|
||||
RevokedCerts []pkix.RevokedCertificate
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Validate input
|
||||
// All flags are required
|
||||
flag.Parse()
|
||||
missing := false
|
||||
flag.VisitAll(func(f *flag.Flag) {
|
||||
if len(f.Value.String()) == 0 {
|
||||
missing = true
|
||||
}
|
||||
})
|
||||
if missing {
|
||||
log.Critical("All flags must be provided.")
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
// Read the issuer cert
|
||||
certPEM, err := ioutil.ReadFile(*certFile)
|
||||
if err != nil {
|
||||
log.Criticalf("Unable to read certificate: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
certBlock, _ := pem.Decode(certPEM)
|
||||
cert, err := x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
log.Criticalf("Unable to parse certificate: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Read the list of revoked certs
|
||||
jsonConfig, err := ioutil.ReadFile(*listFile)
|
||||
if err != nil {
|
||||
log.Criticalf("Unable to read list of revoked certs: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var config Config
|
||||
err = json.Unmarshal(jsonConfig, &config)
|
||||
if err != nil {
|
||||
log.Criticalf("Unable to parse list of revoked certs: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Set up PKCS#11 key
|
||||
priv, err := pkcs11key.New(*module, *token, *pin, *label)
|
||||
if err != nil {
|
||||
log.Criticalf("Unable to instantiate PKCS#11 private key: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Sign the CRL
|
||||
crlDER, err := cert.CreateCRL(rand.Reader, priv, config.RevokedCerts, config.ThisUpdate, config.NextUpdate)
|
||||
if err != nil {
|
||||
log.Criticalf("Error signing certificate: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: "X509 CRL",
|
||||
Bytes: crlDER,
|
||||
})))
|
||||
}
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
// "github.com/cloudflare/cfssl/crypto/pkcs11key"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||
)
|
||||
|
||||
var configFile = flag.String("config", "", "JSON file for subject and validity")
|
||||
var module = flag.String("pkcs11-module", "", "PKCS#11 module")
|
||||
var pin = flag.String("pkcs11-pin", "", "PKCS#11 password")
|
||||
var token = flag.String("pkcs11-token", "", "PKCS#11 token name")
|
||||
var label = flag.String("pkcs11-label", "", "PKCS#11 key label")
|
||||
|
||||
// Config defines the configuration loaded from configFile.
|
||||
type Config struct {
|
||||
Name struct {
|
||||
C string
|
||||
O string
|
||||
OU string
|
||||
CN string
|
||||
}
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Validate input
|
||||
// All flags are required
|
||||
flag.Parse()
|
||||
missing := false
|
||||
flag.VisitAll(func(f *flag.Flag) {
|
||||
if len(f.Value.String()) == 0 {
|
||||
missing = true
|
||||
}
|
||||
})
|
||||
if missing {
|
||||
log.Critical("All flags must be provided.")
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
jsonConfig, err := ioutil.ReadFile(*configFile)
|
||||
if err != nil {
|
||||
log.Criticalf("Unable to read config: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var config Config
|
||||
err = json.Unmarshal(jsonConfig, &config)
|
||||
if err != nil {
|
||||
log.Criticalf("Unable to parse config: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(config.Name.C) == 0 || len(config.Name.O) == 0 ||
|
||||
len(config.Name.CN) == 0 {
|
||||
log.Criticalf("Config must provide country, organizationName, and commonName")
|
||||
return
|
||||
}
|
||||
|
||||
if config.NotBefore.After(config.NotAfter) {
|
||||
log.Criticalf("Invalid validity: notAfter is before notBefore")
|
||||
return
|
||||
}
|
||||
|
||||
// Set up PKCS#11 key
|
||||
priv, err := pkcs11key.New(*module, *token, *pin, *label)
|
||||
if err != nil {
|
||||
log.Criticalf("Unable to instantiate PKCS#11 private key: %v", err)
|
||||
return
|
||||
}
|
||||
pub := priv.Public()
|
||||
|
||||
// Generate serial number
|
||||
serialLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialLimit)
|
||||
if err != nil {
|
||||
log.Criticalf("Error generating serial number: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate subject key ID
|
||||
pubDER, err := x509.MarshalPKIXPublicKey(pub)
|
||||
if err != nil {
|
||||
log.Criticalf("Error serializing public key: %v", err)
|
||||
return
|
||||
}
|
||||
h := sha1.New()
|
||||
h.Write(pubDER)
|
||||
keyID := h.Sum(nil)
|
||||
|
||||
// Sign the certificate
|
||||
rootTemplate := &x509.Certificate{
|
||||
SignatureAlgorithm: x509.SHA256WithRSA,
|
||||
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Country: []string{config.Name.C},
|
||||
Organization: []string{config.Name.O},
|
||||
CommonName: config.Name.CN,
|
||||
},
|
||||
NotBefore: config.NotBefore,
|
||||
NotAfter: config.NotAfter,
|
||||
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
|
||||
SubjectKeyId: keyID,
|
||||
}
|
||||
|
||||
rootDER, err := x509.CreateCertificate(rand.Reader, rootTemplate, rootTemplate, pub, priv)
|
||||
if err != nil {
|
||||
log.Criticalf("Error signing certificate: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: rootDER,
|
||||
})))
|
||||
}
|
||||
10
cmd/shell.go
10
cmd/shell.go
|
|
@ -90,9 +90,6 @@ type Config struct {
|
|||
}
|
||||
|
||||
RA struct {
|
||||
DNSResolver string
|
||||
DNSTimeout string
|
||||
|
||||
// DebugAddr is the address to run the /debug handlers on.
|
||||
DebugAddr string
|
||||
}
|
||||
|
|
@ -106,9 +103,7 @@ type Config struct {
|
|||
}
|
||||
|
||||
VA struct {
|
||||
DNSResolver string
|
||||
DNSTimeout string
|
||||
UserAgent string
|
||||
UserAgent string
|
||||
|
||||
// DebugAddr is the address to run the /debug handlers on.
|
||||
DebugAddr string
|
||||
|
|
@ -178,6 +173,9 @@ type Config struct {
|
|||
// Path to a PEM-encoded copy of the issuer certificate.
|
||||
IssuerCert string
|
||||
MaxKeySize int
|
||||
|
||||
DNSResolver string
|
||||
DNSTimeout string
|
||||
}
|
||||
|
||||
SubscriberAgreementURL string
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ const (
|
|||
)
|
||||
|
||||
func (pd *ProblemDetails) Error() string {
|
||||
return fmt.Sprintf("%v :: %v", pd.Type, pd.Detail)
|
||||
return fmt.Sprintf("%s :: %s", pd.Type, pd.Detail)
|
||||
}
|
||||
|
||||
func cmpStrSlice(a, b []string) bool {
|
||||
|
|
@ -145,13 +145,12 @@ type AcmeIdentifier struct {
|
|||
// URIs pointing to authorizations that should collectively
|
||||
// authorize the certificate being requsted.
|
||||
//
|
||||
// This type is never marshaled, since we only ever receive
|
||||
// it from the client. So it carries some additional information
|
||||
// that is useful internally. (We rely on Go's case-insensitive
|
||||
// JSON unmarshal to properly unmarshal client requests.)
|
||||
// This data is unmarshalled from JSON by way of rawCertificateRequest, which
|
||||
// represents the actual structure received from the client.
|
||||
type CertificateRequest struct {
|
||||
CSR *x509.CertificateRequest // The CSR
|
||||
Authorizations []AcmeURL // Links to Authorization over the account key
|
||||
Bytes []byte // The original bytes of the CSR, for logging.
|
||||
}
|
||||
|
||||
type rawCertificateRequest struct {
|
||||
|
|
@ -173,6 +172,7 @@ func (cr *CertificateRequest) UnmarshalJSON(data []byte) error {
|
|||
|
||||
cr.CSR = csr
|
||||
cr.Authorizations = raw.Authorizations
|
||||
cr.Bytes = raw.CSR
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ func caller(level int) string {
|
|||
func (log *AuditLogger) AuditPanic() {
|
||||
if err := recover(); err != nil {
|
||||
buf := make([]byte, 8192)
|
||||
log.Audit(fmt.Sprintf("Panic caused by err: %v", err))
|
||||
log.Audit(fmt.Sprintf("Panic caused by err: %s", err))
|
||||
|
||||
runtime.Stack(buf, false)
|
||||
log.Audit(fmt.Sprintf("Stack Trace (Current frame) %s", buf))
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization
|
|||
return authz, err
|
||||
}
|
||||
// AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c
|
||||
ra.log.Audit(fmt.Sprintf("Checked CAA records for %s, registration ID %d [Present: %v, Valid for issuance: %v]", identifier.Value, regID, present, valid))
|
||||
ra.log.Audit(fmt.Sprintf("Checked CAA records for %s, registration ID %d [Present: %t, Valid for issuance: %t]", identifier.Value, regID, present, valid))
|
||||
if !valid {
|
||||
err = errors.New("CAA check for identifier failed")
|
||||
return authz, err
|
||||
|
|
|
|||
63
test.sh
63
test.sh
|
|
@ -26,7 +26,7 @@ TESTDIRS="analysis \
|
|||
# Assume first it's the travis commit (for builds of master), unless we're
|
||||
# a PR, when it's actually the first parent.
|
||||
TRIGGER_COMMIT=${TRAVIS_COMMIT}
|
||||
if [ "${TRAVIS_PULL_REQUEST}" != "false" ] ; then
|
||||
if [ "x${TRAVIS_PULL_REQUEST}" != "x" ] ; then
|
||||
revs=$(git rev-list --parents -n 1 HEAD)
|
||||
# The trigger commit is the last ID in the space-delimited rev-list
|
||||
TRIGGER_COMMIT=${revs##* }
|
||||
|
|
@ -54,33 +54,33 @@ update_status() {
|
|||
}
|
||||
|
||||
run() {
|
||||
echo "$*"
|
||||
$* 2>&1
|
||||
echo "$@"
|
||||
"$@" 2>&1
|
||||
local status=$?
|
||||
|
||||
if [ ${status} -eq 0 ]; then
|
||||
update_status --state success
|
||||
echo "Success: $*"
|
||||
echo "Success: $@"
|
||||
else
|
||||
FAILURE=1
|
||||
update_status --state failure
|
||||
echo "[!] FAILURE: $*"
|
||||
echo "[!] FAILURE: $@"
|
||||
fi
|
||||
|
||||
return ${status}
|
||||
}
|
||||
|
||||
run_and_comment() {
|
||||
if [ "x${TRAVIS}" = "x" ] || [ "${TRAVIS_PULL_REQUEST}" == "false" ] || [ ! -f "${GITHUB_SECRET_FILE}"] ; then
|
||||
run $*
|
||||
if [ "x${TRAVIS}" = "x" ] || [ "${TRAVIS_PULL_REQUEST}" == "false" ] || [ ! -f "${GITHUB_SECRET_FILE}" ] ; then
|
||||
run "$@"
|
||||
else
|
||||
result=$(run $*)
|
||||
result=$(run "$@")
|
||||
local status=$?
|
||||
# Only send a comment if exit code > 0
|
||||
if [ ${status} -ne 0 ] ; then
|
||||
echo $'```\n'${result}$'\n```' | github-pr-status --authfile $GITHUB_SECRET_FILE \
|
||||
--owner "letsencrypt" --repo "boulder" \
|
||||
comment --pr ${TRAVIS_PULL_REQUEST}
|
||||
comment --pr "${TRAVIS_PULL_REQUEST}" -b -
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
|
@ -135,13 +135,7 @@ function run_unit_tests() {
|
|||
[ -e $GOBIN/goveralls ] && $GOBIN/goveralls -coverprofile=gover.coverprofile -service=travis-ci
|
||||
else
|
||||
# Run all the tests together if local, for speed
|
||||
dirlist=""
|
||||
|
||||
for dir in ${TESTDIRS}; do
|
||||
dirlist="${dirlist} ./${dir}/"
|
||||
done
|
||||
|
||||
run go test $GOTESTFLAGS -tags pkcs11 ${dirlist}
|
||||
run go test $GOTESTFLAGS -tags pkcs11 ./...
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -169,29 +163,26 @@ end_context #test/golint
|
|||
# Ensure all files are formatted per the `go fmt` tool
|
||||
#
|
||||
start_context "test/gofmt"
|
||||
unformatted=$(find . -name "*.go" -not -path "./Godeps/*" -print | xargs -n1 gofmt -l)
|
||||
if [ "x${unformatted}" == "x" ] ; then
|
||||
update_status --state success
|
||||
else
|
||||
check_gofmt() {
|
||||
unformatted=$(find . -name "*.go" -not -path "./Godeps/*" -print | xargs -n1 gofmt -l)
|
||||
if [ "x${unformatted}" == "x" ] ; then
|
||||
return 0
|
||||
else
|
||||
V="Unformatted files found.
|
||||
Please run 'go fmt' on each of these files and amend your commit to continue."
|
||||
|
||||
V="Unformatted files found.
|
||||
Please run 'go fmt' on each of these files and amend your commit to continue."
|
||||
for f in ${unformatted}; do
|
||||
V=$(printf "%s\n - %s" "${V}" "${f}")
|
||||
done
|
||||
|
||||
for f in ${unformatted}; do
|
||||
V=$(printf "%s\n - %s" "${V}" "${f}")
|
||||
done
|
||||
|
||||
# Print to stdout
|
||||
printf "%s\n\n" "${V}"
|
||||
update_status --state failure --description "${V}"
|
||||
|
||||
# Post a comment with the unformatted list
|
||||
if [ "${TRAVIS_PULL_REQUEST}" != "false" ] ; then
|
||||
run_and_comment printf "%s\n\n" "${V}"
|
||||
# Print to stdout
|
||||
printf "%s\n\n" "${V}"
|
||||
[ "${TRAVIS}" == "true" ] || exit 1 # Stop here if running locally
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
[ "${TRAVIS}" == "true" ] || exit 1 # Stop here if running locally
|
||||
fi
|
||||
run_and_comment check_gofmt
|
||||
end_context #test/gofmt
|
||||
|
||||
#
|
||||
|
|
@ -211,7 +202,7 @@ fi
|
|||
# If the unittests failed, exit before trying to run the integration test.
|
||||
if [ ${FAILURE} != 0 ]; then
|
||||
echo "--------------------------------------------------"
|
||||
echo "--- A unit test failed. ---"
|
||||
echo "--- A unit test or tool failed. ---"
|
||||
echo "--- Stopping before running integration tests. ---"
|
||||
echo "--------------------------------------------------"
|
||||
exit ${FAILURE}
|
||||
|
|
|
|||
|
|
@ -114,8 +114,6 @@
|
|||
},
|
||||
|
||||
"ra": {
|
||||
"dnsResolver": "8.8.8.8:53",
|
||||
"dnsTimeout": "10s",
|
||||
"debugAddr": "localhost:8002"
|
||||
},
|
||||
|
||||
|
|
@ -126,8 +124,6 @@
|
|||
},
|
||||
|
||||
"va": {
|
||||
"dnsResolver": "8.8.8.8:53",
|
||||
"dnsTimeout": "10s",
|
||||
"userAgent": "boulder",
|
||||
"debugAddr": "localhost:8004"
|
||||
},
|
||||
|
|
@ -177,7 +173,9 @@
|
|||
"common": {
|
||||
"baseURL": "http://localhost:4000",
|
||||
"issuerCert": "test/test-ca.pem",
|
||||
"maxKeySize": 4096
|
||||
"maxKeySize": 4096,
|
||||
"dnsResolver": "8.8.8.8:53",
|
||||
"dnsTimeout": "10s"
|
||||
},
|
||||
|
||||
"subscriberAgreementURL": "http://localhost:4300/terms"
|
||||
|
|
|
|||
|
|
@ -101,8 +101,6 @@
|
|||
},
|
||||
|
||||
"ra": {
|
||||
"dnsResolver": "8.8.8.8:53",
|
||||
"dnsTimeout": "10s",
|
||||
"debugAddr": "localhost:8002"
|
||||
},
|
||||
|
||||
|
|
@ -112,6 +110,11 @@
|
|||
"debugAddr": "localhost:8003"
|
||||
},
|
||||
|
||||
"va": {
|
||||
"userAgent": "boulder",
|
||||
"debugAddr": "localhost:8004"
|
||||
},
|
||||
|
||||
"sql": {
|
||||
"SQLDebug": true,
|
||||
"CreateTables": true
|
||||
|
|
@ -156,7 +159,9 @@
|
|||
|
||||
"common": {
|
||||
"baseURL": "http://localhost:4000",
|
||||
"issuerCert": "test/test-ca.pem"
|
||||
"issuerCert": "test/test-ca.pem",
|
||||
"dnsResolver": "8.8.8.8:53",
|
||||
"dnsTimeout": "10s"
|
||||
},
|
||||
|
||||
"subscriberAgreementURL": "http://localhost:4000/terms"
|
||||
|
|
|
|||
|
|
@ -105,8 +105,6 @@
|
|||
},
|
||||
|
||||
"ra": {
|
||||
"dnsResolver": "127.0.0.1:8053",
|
||||
"dnsTimeout": "10s",
|
||||
"debugAddr": "localhost:8002"
|
||||
},
|
||||
|
||||
|
|
@ -117,8 +115,6 @@
|
|||
},
|
||||
|
||||
"va": {
|
||||
"dnsResolver": "127.0.0.1:8053",
|
||||
"dnsTimeout": "10s",
|
||||
"userAgent": "boulder",
|
||||
"debugAddr": "localhost:8004"
|
||||
},
|
||||
|
|
@ -144,7 +140,9 @@
|
|||
"common": {
|
||||
"baseURL": "http://localhost:4300",
|
||||
"issuerCert": "test/test-ca.pem",
|
||||
"maxKeySize": 4096
|
||||
"maxKeySize": 4096,
|
||||
"dnsResolver": "127.0.0.1:8053",
|
||||
"dnsTimeout": "10s"
|
||||
},
|
||||
|
||||
"subscriberAgreementURL": "http://example.com/terms"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ const (
|
|||
BuildIDPath = "/build"
|
||||
)
|
||||
|
||||
// WebFrontEndImpl represents a Boulder web service and its resources
|
||||
type WebFrontEndImpl struct {
|
||||
RA core.RegistrationAuthority
|
||||
SA core.StorageGetter
|
||||
|
|
@ -289,15 +288,16 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool) ([]
|
|||
return nil, nil, reg, errors.New("No body on POST")
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(request.Body)
|
||||
bodyBytes, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
return nil, nil, reg, err
|
||||
}
|
||||
|
||||
body := string(bodyBytes)
|
||||
// Parse as JWS
|
||||
parsedJws, err := jose.ParseSigned(string(body))
|
||||
parsedJws, err := jose.ParseSigned(body)
|
||||
if err != nil {
|
||||
wfe.log.Debug(fmt.Sprintf("Parse error reading JWS: %v", err))
|
||||
wfe.log.Debug(fmt.Sprintf("Parse error reading JWS: %#v", err))
|
||||
return nil, nil, reg, err
|
||||
}
|
||||
|
||||
|
|
@ -312,7 +312,7 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool) ([]
|
|||
return nil, nil, reg, errors.New("Too many signatures on POST")
|
||||
}
|
||||
if len(parsedJws.Signatures) == 0 {
|
||||
wfe.log.Debug(fmt.Sprintf("POST not signed: %v", parsedJws))
|
||||
wfe.log.Debug(fmt.Sprintf("POST not signed: %s", body))
|
||||
return nil, nil, reg, errors.New("POST not signed")
|
||||
}
|
||||
key := parsedJws.Signatures[0].Header.JsonWebKey
|
||||
|
|
@ -624,13 +624,26 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ
|
|||
}
|
||||
}
|
||||
|
||||
func (wfe *WebFrontEndImpl) logCsr(remoteAddr string, cr core.CertificateRequest, registration core.Registration) {
|
||||
var csrLog = struct {
|
||||
RemoteAddr string
|
||||
CsrBase64 []byte
|
||||
Registration core.Registration
|
||||
}{
|
||||
RemoteAddr: remoteAddr,
|
||||
CsrBase64: cr.Bytes,
|
||||
Registration: registration,
|
||||
}
|
||||
wfe.log.AuditObject("Certificate request", csrLog)
|
||||
}
|
||||
|
||||
// NewCertificate is used by clients to request the issuance of a cert for an
|
||||
// authorized identifier.
|
||||
func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request *http.Request) {
|
||||
logEvent := wfe.populateRequestEvent(request)
|
||||
defer wfe.logRequestDetails(&logEvent)
|
||||
|
||||
body, key, reg, err := wfe.verifyPOST(request, true)
|
||||
body, _, reg, err := wfe.verifyPOST(request, true)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
respMsg := malformedJWS
|
||||
|
|
@ -659,14 +672,12 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
|
|||
wfe.sendError(response, "Error unmarshaling certificate request", err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
wfe.logCsr(request.RemoteAddr, init, reg)
|
||||
logEvent.Extra["Authorizations"] = init.Authorizations
|
||||
logEvent.Extra["CSRDNSNames"] = init.CSR.DNSNames
|
||||
logEvent.Extra["CSREmailAddresses"] = init.CSR.EmailAddresses
|
||||
logEvent.Extra["CSRIPAddresses"] = init.CSR.IPAddresses
|
||||
|
||||
wfe.log.Notice(fmt.Sprintf("Client requested new certificate: %v %v %v",
|
||||
request.RemoteAddr, init, key))
|
||||
|
||||
// Create new certificate and return
|
||||
// TODO IMPORTANT: The RA trusts the WFE to provide the correct key. If the
|
||||
// WFE is compromised, *and* the attacker knows the public key of an account
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log/syslog"
|
||||
|
|
@ -128,8 +129,6 @@ wk6Oiadty3eQqSBJv0HnpmiEdQVffIK5Pg4M8Dd+aOBnEkbopAJOuA==
|
|||
"5dd9c885526136d810fc7640f5ba56281e2b75fa3ff7c91a7d23bab7fd4"
|
||||
)
|
||||
|
||||
var log = mocks.UseMockLog()
|
||||
|
||||
type MockSA struct {
|
||||
// empty
|
||||
}
|
||||
|
|
@ -166,8 +165,8 @@ func (sa *MockSA) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration,
|
|||
return core.Registration{ID: 2}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
// Return a fake registration
|
||||
return core.Registration{ID: 1, Agreement: agreementURL}, nil
|
||||
// Return a fake registration. Make sure to fill the key field to avoid marshaling errors.
|
||||
return core.Registration{ID: 1, Key: test1KeyPublic, Agreement: agreementURL}, nil
|
||||
}
|
||||
|
||||
func (sa *MockSA) GetAuthorization(id string) (core.Authorization, error) {
|
||||
|
|
@ -332,6 +331,7 @@ func setupWFE(t *testing.T) WebFrontEndImpl {
|
|||
wfe.NewCert = wfe.BaseURL + NewCertPath
|
||||
wfe.CertBase = wfe.BaseURL + CertPath
|
||||
wfe.SubscriberAgreementURL = agreementURL
|
||||
wfe.log.SyslogWriter = mocks.NewSyslogWriter()
|
||||
|
||||
return wfe
|
||||
}
|
||||
|
|
@ -496,6 +496,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
wfe := setupWFE(t)
|
||||
mux, err := wfe.Handler()
|
||||
test.AssertNotError(t, err, "Problem setting up HTTP handlers")
|
||||
mockLog := wfe.log.SyslogWriter.(*mocks.MockSyslogWriter)
|
||||
|
||||
// TODO: Use a mock RA so we can test various conditions of authorized, not authorized, etc.
|
||||
ra := ra.NewRegistrationAuthorityImpl()
|
||||
|
|
@ -572,9 +573,11 @@ func TestIssueCertificate(t *testing.T) {
|
|||
"{\"type\":\"urn:acme:error:unauthorized\",\"detail\":\"Error creating new cert :: Invalid signature on CSR\"}")
|
||||
|
||||
// Valid, signed JWS body, payload has a CSR with no DNS names
|
||||
mockLog.Clear()
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewCertificate(responseWriter, &http.Request{
|
||||
Method: "POST",
|
||||
Method: "POST",
|
||||
RemoteAddr: "1.1.1.1",
|
||||
Body: makeBody(signRequest(t, `{
|
||||
"authorizations": [],
|
||||
"csr": "MIIBBTCBsgIBADBNMQowCAYDVQQGEwFjMQowCAYDVQQKEwFvMQswCQYDVQQLEwJvdTEKMAgGA1UEBxMBbDEKMAgGA1UECBMBczEOMAwGA1UEAxMFT2ggaGkwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAsr76ZkU2RTqi41eHfmpE5htDvkr202yjRS8x2M5yzT52ooT2WEVtnSuim0YfOEw6f-fHmbqsasqKmqlsJdgz2QIDAQABoAAwCwYJKoZIhvcNAQEFA0EAHkCv4kVPJa53ltOGrhpdH0mT04qHUqiTllJPPjxXxn6iwiVYL8nQuhs4Q2758ENoODBuM2F8gH19TIoXlcm3LQ=="
|
||||
|
|
@ -583,15 +586,18 @@ func TestIssueCertificate(t *testing.T) {
|
|||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
"{\"type\":\"urn:acme:error:unauthorized\",\"detail\":\"Error creating new cert :: Key not authorized for name Oh hi\"}")
|
||||
assertCsrLogged(t, mockLog)
|
||||
|
||||
// Valid, signed JWS body, payload has a valid CSR but no authorizations:
|
||||
// {
|
||||
// "csr": "MIIBK...",
|
||||
// "authorizations: []
|
||||
// }
|
||||
mockLog.Clear()
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewCertificate(responseWriter, &http.Request{
|
||||
Method: "POST",
|
||||
Method: "POST",
|
||||
RemoteAddr: "1.1.1.1",
|
||||
Body: makeBody(signRequest(t, `{
|
||||
"authorizations": [],
|
||||
"csr": "MIIBKzCB2AIBADBNMQowCAYDVQQGEwFjMQowCAYDVQQKEwFvMQswCQYDVQQLEwJvdTEKMAgGA1UEBxMBbDEKMAgGA1UECBMBczEOMAwGA1UEAxMFT2ggaGkwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqvFEGBNrjAotPbcdTSyDpxsESN0-eYl4TqS0ZLYwLTV-FuPHTPjFiq2oH1BEgmRzjb8YiPVXFMnaOeHE7zuuXQIDAQABoCYwJAYJKoZIhvcNAQkOMRcwFTATBgNVHREEDDAKgghtZWVwLmNvbTALBgkqhkiG9w0BAQUDQQBSEcEq-lMUnzv1DO8jK0hJR8YKc0yV8zuWVfAWN0_dsPg5Ny-OHhtJcOTIrUrLTb_xCU7cjiKxU8i3j1kaT-rt"
|
||||
|
|
@ -600,8 +606,9 @@ func TestIssueCertificate(t *testing.T) {
|
|||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
"{\"type\":\"urn:acme:error:unauthorized\",\"detail\":\"Error creating new cert :: Key not authorized for name meep.com\"}")
|
||||
assertCsrLogged(t, mockLog)
|
||||
|
||||
log.Clear()
|
||||
mockLog.Clear()
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewCertificate(responseWriter, &http.Request{
|
||||
Method: "POST",
|
||||
|
|
@ -610,6 +617,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
"authorizations": ["valid"]
|
||||
}`, &wfe.nonceService)),
|
||||
})
|
||||
assertCsrLogged(t, mockLog)
|
||||
randomCertDer, _ := hex.DecodeString(GoodTestCert)
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
|
|
@ -623,7 +631,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
test.AssertEquals(
|
||||
t, responseWriter.Header().Get("Content-Type"),
|
||||
"application/pkix-cert")
|
||||
reqlogs := log.GetAllMatching(`Certificate request - successful`)
|
||||
reqlogs := mockLog.GetAllMatching(`Certificate request - successful`)
|
||||
test.AssertEquals(t, len(reqlogs), 1)
|
||||
test.AssertEquals(t, reqlogs[0].Priority, syslog.LOG_NOTICE)
|
||||
test.AssertContains(t, reqlogs[0].Message, `[AUDIT] `)
|
||||
|
|
@ -1206,5 +1214,37 @@ func TestGetCertificate(t *testing.T) {
|
|||
test.AssertEquals(t, responseWriter.Code, 404)
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Cache-Control"), "public, max-age=0, no-cache")
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Certificate not found"}`)
|
||||
|
||||
}
|
||||
|
||||
func assertCsrLogged(t *testing.T, mockLog *mocks.MockSyslogWriter) {
|
||||
matches := mockLog.GetAllMatching("^\\[AUDIT\\] Certificate request JSON=")
|
||||
test.Assert(t, len(matches) == 1,
|
||||
fmt.Sprintf("Incorrect number of certificate request log entries: %d",
|
||||
len(matches)))
|
||||
test.AssertEquals(t, matches[0].Priority, syslog.LOG_NOTICE)
|
||||
}
|
||||
|
||||
func TestLogCsrPem(t *testing.T) {
|
||||
const certificateRequestJson = `{
|
||||
"csr": "MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAycX3ca-fViOuRWF38mssORISFxbJvspDfhPGRBZDxJ63NIqQzupB-6dp48xkcX7Z_KDaRJStcpJT2S0u33moNT4FHLklQBETLhExDk66cmlz6Xibp3LGZAwhWuec7wJoEwIgY8oq4rxihIyGq7HVIJoq9DqZGrUgfZMDeEJqbphukQOaXGEop7mD-eeu8-z5EVkB1LiJ6Yej6R8MAhVPHzG5fyOu6YVo6vY6QgwjRLfZHNj5XthxgPIEETZlUbiSoI6J19GYHvLURBTy5Ys54lYAPIGfNwcIBAH4gtH9FrYcDY68R22rp4iuxdvkf03ZWiT0F2W1y7_C9B2jayTzvQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAHd6Do9DIZ2hvdt1GwBXYjsqprZidT_DYOMfYcK17KlvdkFT58XrBH88ulLZ72NXEpiFMeTyzfs3XEyGq_Bbe7TBGVYZabUEh-LOskYwhgcOuThVN7tHnH5rhN-gb7cEdysjTb1QL-vOUwYgV75CB6PE5JVYK-cQsMIVvo0Kz4TpNgjJnWzbcH7h0mtvub-fCv92vBPjvYq8gUDLNrok6rbg05tdOJkXsF2G_W-Q6sf2Fvx0bK5JeH4an7P7cXF9VG9nd4sRt5zd-L3IcyvHVKxNhIJXZVH0AOqh_1YrKI9R0QKQiZCEy0xN1okPlcaIVaFhb7IKAHPxTI3r5f72LXY"
|
||||
}`
|
||||
wfe := setupWFE(t)
|
||||
var certificateRequest core.CertificateRequest
|
||||
err := json.Unmarshal([]byte(certificateRequestJson), &certificateRequest)
|
||||
test.AssertNotError(t, err, "Unable to parse certificateRequest")
|
||||
|
||||
mockSA := MockSA{}
|
||||
reg, err := mockSA.GetRegistration(789)
|
||||
test.AssertNotError(t, err, "Unable to get registration")
|
||||
|
||||
remoteAddr := "12.34.98.76"
|
||||
|
||||
wfe.logCsr(remoteAddr, certificateRequest, reg)
|
||||
|
||||
mockLog := wfe.log.SyslogWriter.(*mocks.MockSyslogWriter)
|
||||
matches := mockLog.GetAllMatching("Certificate request")
|
||||
test.Assert(t, len(matches) == 1,
|
||||
"Incorrect number of certificate request log entries")
|
||||
test.AssertEquals(t, matches[0].Priority, syslog.LOG_NOTICE)
|
||||
test.AssertEquals(t, matches[0].Message, `[AUDIT] Certificate request JSON={"RemoteAddr":"12.34.98.76","CsrBase64":"MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAycX3ca+fViOuRWF38mssORISFxbJvspDfhPGRBZDxJ63NIqQzupB+6dp48xkcX7Z/KDaRJStcpJT2S0u33moNT4FHLklQBETLhExDk66cmlz6Xibp3LGZAwhWuec7wJoEwIgY8oq4rxihIyGq7HVIJoq9DqZGrUgfZMDeEJqbphukQOaXGEop7mD+eeu8+z5EVkB1LiJ6Yej6R8MAhVPHzG5fyOu6YVo6vY6QgwjRLfZHNj5XthxgPIEETZlUbiSoI6J19GYHvLURBTy5Ys54lYAPIGfNwcIBAH4gtH9FrYcDY68R22rp4iuxdvkf03ZWiT0F2W1y7/C9B2jayTzvQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAHd6Do9DIZ2hvdt1GwBXYjsqprZidT/DYOMfYcK17KlvdkFT58XrBH88ulLZ72NXEpiFMeTyzfs3XEyGq/Bbe7TBGVYZabUEh+LOskYwhgcOuThVN7tHnH5rhN+gb7cEdysjTb1QL+vOUwYgV75CB6PE5JVYK+cQsMIVvo0Kz4TpNgjJnWzbcH7h0mtvub+fCv92vBPjvYq8gUDLNrok6rbg05tdOJkXsF2G/W+Q6sf2Fvx0bK5JeH4an7P7cXF9VG9nd4sRt5zd+L3IcyvHVKxNhIJXZVH0AOqh/1YrKI9R0QKQiZCEy0xN1okPlcaIVaFhb7IKAHPxTI3r5f72LXY=","Registration":{"id":789,"key":{"kty":"RSA","n":"yNWVhtYEKJR21y9xsHV-PD_bYwbXSeNuFal46xYxVfRL5mqha7vttvjB_vc7Xg2RvgCxHPCqoxgMPTzHrZT75LjCwIW2K_klBYN8oYvTwwmeSkAz6ut7ZxPv-nZaT5TJhGk0NT2kh_zSpdriEJ_3vW-mqxYbbBmpvHqsa1_zx9fSuHYctAZJWzxzUZXykbWMWQZpEiE0J4ajj51fInEzVn7VxV-mzfMyboQjujPh7aNJxAWSq4oQEJJDgWwSh9leyoJoPpONHxh5nEE5AjE01FkGICSxjpZsF-w8hOTI3XXohUdu29Se26k2B0PolDSuj0GIQU6-W9TdLXSjBb2SpQ","e":"AAEAAQ"},"recoveryToken":"","agreement":"http://example.invalid/terms"}}`)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue