func (h *certRequestHandler) validateCert(cert *ssh.Certificate, authorizedSigners map[string]string) error { var certChecker ssh.CertChecker certChecker.IsAuthority = func(auth ssh.PublicKey) bool { fingerprint := ssh_ca_util.MakeFingerprint(auth.Marshal()) _, ok := authorizedSigners[fingerprint] return ok } err := certChecker.CheckCert(cert.ValidPrincipals[0], cert) if err != nil { err := fmt.Errorf("Cert not valid: %v", err) return err } return nil }
// AuthKey authenticates based on a public key. // // Params: // - metadata (ssh.ConnMetadata) // - key (ssh.PublicKey) // - authorizedKeys ([]string): List of lines from an authorized keys file. // // Returns: // *ssh.Permissions // func AuthKey(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) { meta := p.Get("metadata", nil).(ssh.ConnMetadata) key := p.Get("key", nil).(ssh.PublicKey) authorized := p.Get("authorizedKeys", []string{}).([]string) auth := new(ssh.CertChecker) auth.UserKeyFallback = func(meta ssh.ConnMetadata, pk ssh.PublicKey) (*ssh.Permissions, error) { // This gives us a string in the form "ssh-rsa LONG_KEY" suppliedType := key.Type() supplied := key.Marshal() for _, allowedKey := range authorized { allowed, _, _, _, err := ssh.ParseAuthorizedKey([]byte(allowedKey)) if err != nil { log.Infof(c, "Could not parse authorized key '%q': %s", allowedKey, err) continue } // We use a contstant time compare more as a precaution than anything // else. A timing attack here would be very difficult, but... better // safe than sorry. if allowed.Type() == suppliedType && subtle.ConstantTimeCompare(allowed.Marshal(), supplied) == 1 { log.Infof(c, "Key accepted for user %s.", meta.User()) perm := &ssh.Permissions{ Extensions: map[string]string{ "user": meta.User(), }, } return perm, nil } } return nil, fmt.Errorf("No matching keys found.") } return auth.Authenticate(meta, key) }