// LoadConfig attempts to load the configuration from a byte slice. // On error, it returns nil. func LoadConfig(config []byte) (*Config, error) { var cfg = &Config{} err := json.Unmarshal(config, &cfg) if err != nil { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to unmarshal configuration: "+err.Error())) } if cfg.Signing == nil { return nil, errors.New("No \"signing\" field present") } if cfg.Signing.Default == nil { log.Debugf("no default given: using default config") cfg.Signing.Default = DefaultConfig() } else { if err := cfg.Signing.Default.populate(cfg); err != nil { return nil, err } } for k := range cfg.Signing.Profiles { if err := cfg.Signing.Profiles[k].populate(cfg); err != nil { return nil, err } } if !cfg.Valid() { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid configuration")) } log.Debugf("configuration ok") return cfg, nil }
// BundleFromRemote fetches the certificate chain served by the server at // serverName (or ip, if the ip argument is not the empty string). It // is expected that the method will be able to make a connection at // port 443. The chain used by the server in this connection is // used to rebuild the bundle. func (b *Bundler) BundleFromRemote(serverName, ip string) (*Bundle, error) { config := &tls.Config{ RootCAs: b.RootPool, ServerName: serverName, } // Dial by IP if present var dialName string if ip != "" { dialName = ip + ":443" } else { dialName = serverName + ":443" } log.Debugf("bundling from remote %s", dialName) conn, err := tls.Dial("tcp", dialName, config) var dialError string // If there's an error in tls.Dial, try again with // InsecureSkipVerify to fetch the remote bundle to (re-)bundle with. // If the bundle is indeed not usable (expired, mismatched hostnames, etc.), // report the error. // Otherwise, create a working bundle and insert the tls error in the bundle.Status. if err != nil { log.Debugf("dial failed: %v", err) // record the error msg dialError = fmt.Sprintf("Failed rigid TLS handshake with %s: %v", dialName, err) // dial again with InsecureSkipVerify log.Debugf("try again with InsecureSkipVerify.") config.InsecureSkipVerify = true conn, err = tls.Dial("tcp", dialName, config) if err != nil { log.Debugf("dial with InsecureSkipVerify failed: %v", err) return nil, errors.New(errors.DialError, errors.Unknown, err) } } connState := conn.ConnectionState() certs := connState.PeerCertificates err = conn.VerifyHostname(serverName) if err != nil { log.Debugf("failed to verify hostname: %v", err) return nil, errors.New(errors.CertificateError, errors.VerifyFailed, err) } // verify peer intermediates and store them if there is any missing from the bundle. // Don't care if there is error, will throw it any way in Bundle() call. b.fetchIntermediates(certs) // Bundle with remote certs. Inject the initial dial error, if any, to the status reporting. bundle, err := b.Bundle(certs, nil, Ubiquitous) if err != nil { return nil, err } else if dialError != "" { bundle.Status.Messages = append(bundle.Status.Messages, dialError) } return bundle, err }
// BundleFromRemote fetches the certificate served by the server at // serverName (or ip, if the ip argument is not the empty string). It // is expected that the method will be able to make a connection at // port 443. The certificate used by the server in this connection is // used to build the bundle, which will necessarily be keyless. func (b *Bundler) BundleFromRemote(serverName, ip string, flavor BundleFlavor) (*Bundle, error) { config := &tls.Config{ RootCAs: b.RootPool, ServerName: serverName, } // Dial by IP if present var dialName string if ip != "" { dialName = ip + ":443" } else { dialName = serverName + ":443" } log.Debugf("bundling from remote %s", dialName) dialer := &net.Dialer{Timeout: time.Duration(5) * time.Second} conn, err := tls.DialWithDialer(dialer, "tcp", dialName, config) var dialError string // If there's an error in tls.Dial, try again with // InsecureSkipVerify to fetch the remote bundle to (re-)bundle // with. If the bundle is indeed not usable (expired, mismatched // hostnames, etc.), report the error. Otherwise, create a // working bundle and insert the tls error in the bundle.Status. if err != nil { log.Debugf("dial failed: %v", err) // record the error msg dialError = fmt.Sprintf("Failed rigid TLS handshake with %s: %v", dialName, err) // dial again with InsecureSkipVerify log.Debugf("try again with InsecureSkipVerify.") config.InsecureSkipVerify = true conn, err = tls.DialWithDialer(dialer, "tcp", dialName, config) if err != nil { log.Debugf("dial with InsecureSkipVerify failed: %v", err) return nil, errors.Wrap(errors.DialError, errors.Unknown, err) } } connState := conn.ConnectionState() certs := connState.PeerCertificates err = conn.VerifyHostname(serverName) if err != nil { log.Debugf("failed to verify hostname: %v", err) return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err) } // Bundle with remote certs. Inject the initial dial error, if any, to the status reporting. bundle, err := b.Bundle(certs, nil, flavor) if err != nil { return nil, err } else if dialError != "" { bundle.Status.Messages = append(bundle.Status.Messages, dialError) } return bundle, err }
func TestListener(t *testing.T) { var before = 55 * time.Second trl, err := New(before, testLIdentity) if err != nil { t.Fatalf("failed to set up transport: %v", err) } trl.Identity.Request.CN = "localhost test server" err = trl.RefreshKeys() if err != nil { t.Fatalf("%v", err) } l, err = Listen("127.0.0.1:8765", trl) if err != nil { t.Fatalf("%v", err) } errChan := make(chan error, 0) go func() { err := <-errChan if err != nil { t.Fatalf("listener auto update failed: %v", err) } }() cert := trl.Provider.Certificate() before = cert.NotAfter.Sub(time.Now()) before -= 5 * time.Second trl.Before = before go l.AutoUpdate(nil, errChan) go testListen(t) <-time.After(1 * time.Second) log.Debug("dialer making connection") conn, err := Dial("127.0.0.1:8765", tr) if err != nil { log.Debugf("certificate time: %s-%s / %s", trl.Provider.Certificate().NotBefore, trl.Provider.Certificate().NotAfter, time.Now().UTC()) log.Debugf("%#v", trl.Provider.Certificate()) t.Fatalf("%v", err) } log.Debugf("client connected to server") conn.Close() }
// Signing specifically validates the signature policies. func (s *Signing) Valid() bool { log.Debugf("validating configuration") if !s.Default.validProfile(true) { log.Debugf("default profile is invalid") return false } else { for _, p := range s.Profiles { if !p.validProfile(false) { log.Debugf("invalid profile") return false } } } return true }
func (b *Bundler) verifyChain(chain []*fetchedIntermediate) bool { // This process will verify if the root of the (partial) chain is in our root pool, // and will fail otherwise. log.Debugf("verifying chain") for vchain := chain[:]; len(vchain) > 0; vchain = vchain[1:] { cert := vchain[0] // If this is a certificate in one of the pools, skip it. if b.KnownIssuers[string(cert.Cert.Signature)] { log.Debugf("certificate is known") continue } _, err := cert.Cert.Verify(b.VerifyOptions()) if err != nil { log.Debugf("certificate failed verification: %v", err) return false } else if len(chain) == len(vchain) && isChainRootNode(cert.Cert) { // The first certificate in the chain is a root; it shouldn't be stored. log.Debug("looking at root certificate, will not store") continue } // leaf cert has an empty name, don't store leaf cert. if cert.Name == "" { continue } log.Debug("add certificate to intermediate pool:", cert.Name) b.IntermediatePool.AddCert(cert.Cert) b.KnownIssuers[string(cert.Cert.Signature)] = true if IntermediateStash != "" { fileName := filepath.Join(IntermediateStash, cert.Name) var block = pem.Block{Type: "CERTIFICATE", Bytes: cert.Cert.Raw} log.Debugf("write intermediate to stash directory: %s", fileName) // If the write fails, verification should not fail. err = ioutil.WriteFile(fileName, pem.EncodeToMemory(&block), 0644) if err != nil { log.Errorf("failed to write new intermediate: %v", err) } else { log.Info("stashed new intermediate ", cert.Name) } } } return true }
// DialAny smartly chooses one of the keyless servers given. (Opting to reuse an existing connection if possible) func (c *Client) DialAny(ski gokeyless.SKI) (*gokeyless.Conn, error) { servers := c.getServers(ski) if len(servers) == 0 { return nil, fmt.Errorf("no servers registered for SKI %02x", ski) } for _, server := range servers { c.m.RLock() conn, ok := c.conns[server] c.m.RUnlock() if ok { if conn.Use() { return conn, nil } c.m.Lock() if c.conns[server] == conn { delete(c.conns, server) } c.m.Unlock() } } // choose from possible servers at random until a connection can be established. for len(servers) > 0 { n := rand.Intn(len(servers)) conn, err := c.Dial(servers[n]) if err == nil { return conn, nil } log.Debugf("Couldn't dial server %s: %v", servers[n], err) servers = append(servers[:n], servers[n+1:]...) } return nil, errors.New("couldn't dial any of the registered servers") }
// Dial retuns a (reused/reusable) connection to a keyless server. func (c *Client) Dial(server string) (*gokeyless.Conn, error) { if c.Config == nil { return nil, errors.New("gokeyless/client: TLS client has not yet been initialized with certificate and keyserver CA") } c.m.RLock() conn, ok := c.conns[server] c.m.RUnlock() if ok { if conn.Use() { return conn, nil } c.m.Lock() if c.conns[server] == conn { delete(c.conns, server) } c.m.Unlock() } log.Debugf("Dialing %s\n", server) inner, err := tls.Dial("tcp", server, c.Config) if err != nil { return nil, err } c.m.Lock() defer c.m.Unlock() c.conns[server] = gokeyless.NewConn(inner) return c.conns[server], nil }
// Generate generates a key as specified in the request. Currently, // only ECDSA and RSA are supported. func (kr *KeyRequest) Generate() (interface{}, error) { log.Debugf("generate key from request: algo=%s, size=%d", kr.Algo, kr.Size) switch kr.Algo { case "rsa": if kr.Size < 2048 { return nil, errors.New("RSA key is too weak") } return rsa.GenerateKey(rand.Reader, kr.Size) case "ecdsa": var curve elliptic.Curve switch kr.Size { case curveP256: curve = elliptic.P256() case curveP384: curve = elliptic.P384() case curveP521: curve = elliptic.P521() default: return nil, errors.New("invalid curve") } return ecdsa.GenerateKey(curve, rand.Reader) default: return nil, errors.New("invalid algorithm") } }
// Generate generates a key as specified in the request. Currently, // only ECDSA and RSA are supported. func (kr *BasicKeyRequest) Generate() (crypto.PrivateKey, error) { log.Debugf("generate key from request: algo=%s, size=%d", kr.Algo, kr.Size) switch kr.Algo() { case "rsa": if kr.Size() < 2048 { return nil, errors.New("RSA key is too weak") } if kr.Size() > 8192 { return nil, errors.New("RSA key size too large") } return rsa.GenerateKey(rand.Reader, kr.Size()) case "ecdsa": var curve elliptic.Curve switch kr.Size() { case curveP256: curve = elliptic.P256() case curveP384: curve = elliptic.P384() case curveP521: curve = elliptic.P521() default: return nil, errors.New("invalid curve") } return ecdsa.GenerateKey(curve, rand.Reader) default: return nil, errors.New("invalid algorithm") } }
// Add adds a new key to the server's internal repertoire. // Stores in maps by SKI and (if possible) Digest, SNI, Server IP, and Client IP. func (keys *defaultKeystore) Add(op *gokeyless.Operation, priv crypto.Signer) error { ski, err := gokeyless.GetSKI(priv.Public()) if err != nil { return err } keys.Lock() defer keys.Unlock() if digest, err := gokeyless.GetDigest(priv.Public()); err == nil { keys.digests[digest] = ski } if op != nil { if op.SNI != "" { keys.snis[op.SNI] = ski } if op.ServerIP != nil { keys.serverIPs[op.ServerIP.String()] = ski } if op.ClientIP != nil { keys.clientIPs[op.ClientIP.String()] = ski } keys.validAKIs[ski] = keys.validAKIs[ski].Add(op.AKI) } keys.skis[ski] = priv log.Debugf("Adding key with SKI: %02x", ski) return nil }
func (ctx *context) copyResults(timeout time.Duration) map[string]FamilyResult { var timedOut bool done := make(chan bool, 1) results := make(map[string]FamilyResult) go func() { for result := range ctx.resultChan { if timedOut { log.Debugf("Received result after timeout: %v", result) continue } if results[result.Family] == nil { results[result.Family] = make(FamilyResult) } results[result.Family][result.Scanner] = result.ScannerResult } done <- true }() select { case <-done: case <-time.After(timeout): timedOut = true log.Warningf("Scan timed out after %v", timeout) } return results }
// LoadRootCAs loads the default root certificate authorities from file. func LoadRootCAs(caBundleFile string) (err error) { if caBundleFile != "" { log.Debugf("Loading scan RootCAs: %s", caBundleFile) RootCAs, err = helpers.LoadPEMCertPool(caBundleFile) } return }
// LoadFile attempts to load the db configuration file stored at the path // and returns the configuration. On error, it returns nil. func LoadFile(path string) (cfg *DBConfig, err error) { log.Debugf("loading db configuration file from %s", path) if path == "" { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid path")) } var body []byte body, err = ioutil.ReadFile(path) if err != nil { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("could not read configuration file")) } cfg = &DBConfig{} err = json.Unmarshal(body, &cfg) if err != nil { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to unmarshal configuration: "+err.Error())) } if cfg.DataSourceName == "" || cfg.DriverName == "" { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid db configuration")) } return }
// registerSKI associates the SKI of a public key with a particular keyserver. func (c *Client) registerSKI(server string, ski gokeyless.SKI) (err error) { log.Debugf("Registering key @ %s with SKI: %02x", server, ski) var r Remote if server == "" { r = c.DefaultRemote if r == nil { err = errors.New("no default remote") log.Error(err) return } } else { var ok bool c.m.RLock() r, ok = c.servers[server] c.m.RUnlock() if ok { server = "" } else { if r, err = c.LookupServer(server); err != nil { return } } } c.AddRemote(server, r, ski) return }
func TestAutoUpdate(t *testing.T) { // To force a refresh, make sure that the certificate is // updated 5 seconds from now. cert := tr.Provider.Certificate() if cert == nil { t.Fatal("no certificate from provider") } certUpdates := make(chan time.Time, 0) errUpdates := make(chan error, 0) oldBefore := tr.Before before := cert.NotAfter.Sub(time.Now()) before -= 5 * time.Second tr.Before = before defer func() { tr.Before = oldBefore PollInterval = 30 * time.Second }() PollInterval = 2 * time.Second go tr.AutoUpdate(certUpdates, errUpdates) log.Debugf("waiting for certificate update or error from auto updater") select { case <-certUpdates: // Nothing needs to be done case err := <-errUpdates: t.Fatalf("%v", err) case <-time.After(15 * time.Second): t.Fatal("timeout waiting for update") } }
// AutoUpdate will automatically update the listener. If a non-nil // certUpdates chan is provided, it will receive timestamps for // reissued certificates. If errChan is non-nil, any errors that occur // in the updater will be passed along. func (l *Listener) AutoUpdate(certUpdates chan<- time.Time, errChan chan<- error) { defer func() { if r := recover(); r != nil { log.Criticalf("AutoUpdate panicked: %v", r) } }() for { // Wait until it's time to update the certificate. target := time.Now().Add(l.Lifespan()) if PollInterval == 0 { <-time.After(l.Lifespan()) } else { pollWait(target) } // Keep trying to update the certificate until it's // ready. for { log.Debug("refreshing certificate") err := l.RefreshKeys() if err == nil { break } delay := l.Transport.Backoff.Duration() log.Debugf("failed to update certificate, will try again in %s", delay) if errChan != nil { errChan <- err } <-time.After(delay) } if certUpdates != nil { certUpdates <- time.Now() } config, err := l.getConfig() if err != nil { log.Debug("immediately after getting a new certificate, the Transport is reporting errors: %v", err) if errChan != nil { errChan <- err } } address := l.Listener.Addr().String() lnet := l.Listener.Addr().Network() l.Listener, err = tls.Listen(lnet, address, config) if err != nil { log.Debug("immediately after getting a new certificate, the Transport is reporting errors: %v", err) if errChan != nil { errChan <- err } } log.Debug("listener: auto update of certificate complete") l.Transport.Backoff.Reset() } }
func TestLoadBadRootConfs(t *testing.T) { confs := []string{ "testdata/roots_bad_db.conf", "testdata/roots_bad_certificate.conf", "testdata/roots_bad_private_key.conf", "testdata/roots_badconfig.conf", "testdata/roots_badspec.conf", "testdata/roots_badspec2.conf", "testdata/roots_badspec3.conf", "testdata/roots_bad_whitelist.conf", "testdata/roots_bad_whitelist.conf2", "testdata/roots_missing_certificate.conf", "testdata/roots_missing_certificate_entry.conf", "testdata/roots_missing_private_key.conf", "testdata/roots_missing_private_key_entry.conf", } for _, cf := range confs { _, err := Parse(cf) if err == nil { t.Fatalf("expected config file %s to fail", cf) } log.Debugf("%s: %v", cf, err) } }
// parse, and the ExpiryString parameter, are needed to parse // expiration timestamps from JSON. The JSON decoder is not able to // decode a string time duration to a time.Duration, so this is called // when loading the configuration to properly parse and fill out the // Expiry parameter. It returns true if there was a valid string // representation of a time.Duration, and false if an error occurred. func (p *SigningProfile) parse() bool { log.Debugf("parse expiry in profile") if p == nil { log.Debugf("failed: no timestamp in profile") return false } else if p.ExpiryString == "" { log.Debugf("failed: empty expiry string") return false } else if dur, err := time.ParseDuration(p.ExpiryString); err != nil { log.Debugf("failed to parse expiry: %v", err) return false } else { log.Debugf("expiry is valid") p.Expiry = dur return true } }
func (tr *Transport) getCertificate() (cert tls.Certificate, err error) { if !tr.Provider.Ready() { log.Debug("transport isn't ready; attempting to refresh keypair") err = tr.RefreshKeys() if err != nil { log.Debugf("transport couldn't get a certificate: %v", err) return } } cert, err = tr.Provider.X509KeyPair() if err != nil { log.Debugf("couldn't generate an X.509 keypair: %v", err) } return }
// Scan performs the scan to be performed on the given host and stores its result. func (s *Scanner) Scan(addr, hostname string) (Grade, Output, error) { grade, output, err := s.scan(addr, hostname) if err != nil { log.Debugf("scan: %v", err) return grade, output, err } return grade, output, err }
// fetchRemoteCertificate retrieves a single URL pointing to a certificate // and attempts to first parse it as a DER-encoded certificate; if // this fails, it attempts to decode it as a PEM-encoded certificate. func fetchRemoteCertificate(certURL string) (fi *fetchedIntermediate, err error) { log.Debugf("fetching remote certificate: %s", certURL) var resp *http.Response resp, err = http.Get(certURL) if err != nil { log.Debugf("failed HTTP get: %v", err) return } defer resp.Body.Close() var certData []byte certData, err = ioutil.ReadAll(resp.Body) if err != nil { log.Debugf("failed to read response body: %v", err) return } log.Debugf("attempting to parse certificate as DER") crt, err := x509.ParseCertificate(certData) if err != nil { log.Debugf("attempting to parse certificate as PEM") crt, err = helpers.ParseCertificatePEM(certData) if err != nil { log.Debugf("failed to parse certificate: %v", err) return } } log.Debugf("certificate fetch succeeds") fi = &fetchedIntermediate{Cert: crt, Name: constructCertFileName(crt)} return }
// Valid checks the signature policies, ensuring they are valid // policies. A policy is valid if it has defined at least key usages // to be used, and a valid default profile has defined at least a // default expiration. func (p *Signing) Valid() bool { if p == nil { return false } log.Debugf("validating configuration") if !p.Default.validProfile(true) { log.Debugf("default profile is invalid") return false } for _, sp := range p.Profiles { if !sp.validProfile(false) { log.Debugf("invalid profile") return false } } return true }
// A valid profile has defined at least key usages to be used, and a // valid default profile has defined at least a default expiration. func (p *SigningProfile) validProfile(isDefault bool) bool { log.Debugf("validate profile") if !isDefault { if len(p.Usage) == 0 { log.Debugf("invalid profile: no usages specified") return false } else if _, _, unk := p.Usages(); len(unk) == len(p.Usage) { log.Debugf("invalid profile: no valid usages") return false } } else { if p.Expiry == 0 { log.Debugf("invalid profile: no expiry set") return false } } log.Debugf("profile is valid") return true }
// registerSKI associates the SKI of a public key with a particular keyserver. func (c *Client) registerSKI(server string, ski gokeyless.SKI) { if server == "" { server = c.DefaultServer } log.Debugf("Registering key @ %s with SKI: %02x", server, ski) c.m.Lock() defer c.m.Unlock() c.allServers[ski] = append(c.allServers[ski], server) }
// AutoUpdate will automatically update the listener. If a non-nil // certUpdates chan is provided, it will receive timestamps for // reissued certificates. If errChan is non-nil, any errors that occur // in the updater will be passed along. func (tr *Transport) AutoUpdate(certUpdates chan<- time.Time, errChan chan<- error) { defer func() { if r := recover(); r != nil { log.Criticalf("AutoUpdate panicked: %v", r) } }() for { // Wait until it's time to update the certificate. target := time.Now().Add(tr.Lifespan()) if PollInterval == 0 { <-time.After(tr.Lifespan()) } else { pollWait(target) } // Keep trying to update the certificate until it's // ready. for { log.Debugf("attempting to refresh keypair") err := tr.RefreshKeys() if err == nil { break } delay := tr.Backoff.Duration() log.Debugf("failed to update certificate, will try again in %s", delay) if errChan != nil { errChan <- err } <-time.After(delay) } log.Debugf("certificate updated") if certUpdates != nil { certUpdates <- time.Now() } tr.Backoff.Reset() } }
// LoadFile attempts to load the configuration file stored at the path // and returns the configuration. On error, it returns nil. func LoadFile(path string) *Config { log.Debugf("loading configuration file from %s", path) if path == "" { return nil } body, err := ioutil.ReadFile(path) if err != nil { log.Debugf("failed to read configuration file: %v", err) return nil } var cfg = &Config{} err = json.Unmarshal(body, &cfg) if err != nil { log.Debugf("failed to unmarshal configuration: %v", err) return nil } if cfg.Signing.Default == nil { log.Debugf("no default given: using default config") cfg.Signing.Default = DefaultConfig() } else { if !cfg.Signing.Default.parse() { return nil } } if !cfg.Valid() { return nil } else { for k := range cfg.Signing.Profiles { if !cfg.Signing.Profiles[k].parse() { return nil } } } log.Debugf("configuration ok") return cfg }
func testListen(t *testing.T) { log.Debug("listener waiting for connection") conn, err := l.Accept() if err != nil { t.Fatalf("%v", err) } log.Debugf("client has connected") conn.Write([]byte("hello")) conn.Close() }
// UnmarshalJSON unmarshals a JSON string into an OID. func (oid *OID) UnmarshalJSON(data []byte) (err error) { if data[0] != '"' || data[len(data)-1] != '"' { return errors.New("OID JSON string not wrapped in quotes." + string(data)) } data = data[1 : len(data)-1] parsedOid, err := parseObjectIdentifier(string(data)) if err != nil { return err } *oid = OID(parsedOid) log.Debugf("Parsed OID %v", *oid) return }
// LoadFile attempts to load the configuration file stored at the path // and returns the configuration. On error, it returns nil. func LoadFile(path string) (*Config, error) { log.Debugf("loading configuration file from %s", path) if path == "" { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid path")) } body, err := ioutil.ReadFile(path) if err != nil { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("could not read configuration file")) } return LoadConfig(body) }